4 * Copyright 1996 Ulrich Schmid
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
32 #define GET_USHORT(buffer, i)\
33 (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1])))
34 #define GET_SHORT(buffer, i)\
35 (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1])))
36 #define GET_UINT(buffer, i)\
37 GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i+2)
39 static HLPFILE *first_hlpfile = 0;
40 static BYTE *file_buffer;
69 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
70 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
71 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
72 static BOOL HLPFILE_SystemCommands(HLPFILE*);
73 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end);
74 static BYTE* HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr);
75 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE*);
76 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE*);
77 static BOOL HLPFILE_UncompressLZ77_Topic(HLPFILE*);
78 static BOOL HLPFILE_GetContext(HLPFILE*);
79 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
80 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
81 static UINT HLPFILE_Uncompressed2_Size(BYTE*, BYTE*);
82 static void HLPFILE_Uncompress2(BYTE**, BYTE*, BYTE*);
83 static BOOL HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
84 static void HLPFILE_UncompressRLE(const BYTE* src, unsigned sz, BYTE** dst);
85 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile);
87 /***********************************************************************
91 HLPFILE_PAGE *HLPFILE_Contents(LPCSTR lpszPath)
93 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
95 if (!hlpfile) return 0;
97 return hlpfile->first_page;
100 /***********************************************************************
102 * HLPFILE_PageByNumber
104 HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
107 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
109 if (!hlpfile) return 0;
111 WINE_TRACE("[%s/%u]\n", lpszPath, wNum);
113 for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
118 /***********************************************************************
120 * HLPFILE_HlpFilePageByHash
122 HLPFILE_PAGE *HLPFILE_PageByHash(LPCSTR lpszPath, LONG lHash)
126 HLPFILE* hlpfile = HLPFILE_ReadHlpFile(lpszPath);
129 WINE_TRACE("path<%s>[%lx]\n", lpszPath, lHash);
131 if (!hlpfile) return 0;
134 for (i = 0; i < hlpfile->wContextLen; i++)
136 if (hlpfile->Context[i].lHash != lHash) continue;
139 * this finds the page containing the offset. The offset can either
140 * refer to the top of the page (offset == page->offset), or
141 * to some paragraph inside the page...
142 * As of today, we only return the page... we should also return
143 * a paragraph, and then, while opening a new page, compute the
144 * y-offset of the paragraph to be shown and scroll the window
148 for (page = hlpfile->first_page; page; page = page->next)
150 if (page->offset <= hlpfile->Context[i].offset)
152 if (!found || found->offset < page->offset)
156 if (found) return found;
158 WINE_ERR("Page of offset %lu not found in file %s\n",
159 hlpfile->Context[i].offset, lpszPath);
162 WINE_ERR("Page of hash %lx not found in file %s\n", lHash, lpszPath);
166 /***********************************************************************
170 LONG HLPFILE_Hash(LPCSTR lpszContext)
175 while ((c = *lpszContext++))
178 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
179 if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
180 if (c >= '1' && c <= '9') x = c - '0';
181 if (c == '0') x = 10;
182 if (c == '.') x = 12;
183 if (c == '_') x = 13;
184 if (x) lHash = lHash * 43 + x;
188 /***********************************************************************
190 * HLPFILE_ReadHlpFile
192 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
196 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
198 if (!lstrcmp(hlpfile->lpszPath, lpszPath))
200 hlpfile->wRefCount++;
205 hlpfile = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
206 if (!hlpfile) return 0;
208 hlpfile->wRefCount = 1;
209 hlpfile->Context = NULL;
210 hlpfile->wContextLen = 0;
211 hlpfile->first_page = NULL;
212 hlpfile->first_macro = NULL;
213 hlpfile->prev = NULL;
214 hlpfile->next = first_hlpfile;
215 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
216 hlpfile->lpszTitle = NULL;
218 hlpfile->numFonts = 0;
219 hlpfile->fonts = NULL;
221 strcpy(hlpfile->lpszPath, lpszPath);
223 first_hlpfile = hlpfile;
224 if (hlpfile->next) hlpfile->next->prev = hlpfile;
226 phrases.offsets = NULL;
227 phrases.buffer = NULL;
232 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
234 HLPFILE_FreeHlpFile(hlpfile);
238 if (phrases.offsets) HeapFree(GetProcessHeap(), 0, phrases.offsets);
239 if (phrases.buffer) HeapFree(GetProcessHeap(), 0, phrases.buffer);
240 if (topic.map) HeapFree(GetProcessHeap(), 0, topic.map);
241 if (file_buffer) HeapFree(GetProcessHeap(), 0, file_buffer);
246 /***********************************************************************
248 * HLPFILE_DoReadHlpFile
250 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
257 unsigned index, old_index, offset, len, offs;
259 hFile = OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH);
260 if (hFile == HFILE_ERROR) return FALSE;
262 ret = HLPFILE_ReadFileToBuffer(hFile);
264 if (!ret) return FALSE;
266 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
267 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile) &&
268 !HLPFILE_Uncompress_Phrases40(hlpfile))
270 if (!HLPFILE_UncompressLZ77_Topic(hlpfile)) return FALSE;
271 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
280 /* FIXME this depends on the blocksize, can be 2k in some cases */
281 index = (ref - 0x0C) >> 14;
282 offset = (ref - 0x0C) & 0x3fff;
284 WINE_TRACE("ref=%08lx => [%u/%u]\n", ref, index, offset);
286 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
287 buf = topic.map[index] + offset;
288 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
289 end = min(buf + GET_UINT(buf, 0), topic.end);
290 if (index != old_index) {offs = 0; old_index = index;}
295 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
299 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
304 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
309 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
312 ref = GET_UINT(buf, 0xc);
313 } while (ref != 0xffffffff);
315 return HLPFILE_GetContext(hlpfile);
318 /***********************************************************************
322 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
328 if (buf + 0x31 > end) {WINE_WARN("page1\n"); return FALSE;};
329 title = buf + GET_UINT(buf, 0x10);
330 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
332 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
334 if (hlpfile->hasPhrases)
336 titlesize = HLPFILE_Uncompressed2_Size(title, end);
337 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize);
338 if (!page) return FALSE;
340 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
341 HLPFILE_Uncompress2(&title, end, page->lpszTitle);
345 titlesize = GET_UINT(buf, 4) + 1;
346 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize);
347 if (!page) return FALSE;
348 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
350 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize, title, end);
355 titlesize = GET_UINT(buf, 0x4);
356 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize);
357 if (!page) return FALSE;
359 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
360 memcpy(page->lpszTitle, title, titlesize);
363 if (hlpfile->first_page)
367 for (p = hlpfile->first_page; p->next; p = p->next);
373 hlpfile->first_page = page;
377 page->file = hlpfile;
379 page->first_paragraph = NULL;
380 page->wNumber = GET_UINT(buf, 0x21);
381 page->offset = offset;
383 WINE_TRACE("Added page[%d]: title='%s' offset=%08x\n",
384 page->wNumber, page->lpszTitle, page->offset);
386 memset(&attributes, 0, sizeof(attributes));
391 static long fetch_long(BYTE** ptr)
397 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
402 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
409 static unsigned long fetch_ulong(BYTE** ptr)
415 ret = *(unsigned long*)(*ptr) / 2;
420 ret = *(unsigned short*)(*ptr) / 2;
426 static short fetch_short(BYTE** ptr)
432 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
437 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
443 static unsigned short fetch_ushort(BYTE** ptr)
449 ret = *(unsigned short*)(*ptr) / 2;
454 ret = *(unsigned char*)(*ptr) / 2;
460 /******************************************************************
461 * HLPFILE_LoadPictureByAddr
465 static BOOL HLPFILE_LoadPictureByAddr(HLPFILE *hlpfile, char* ref,
466 unsigned long size, unsigned pos)
470 numpict = *(unsigned short*)(ref + 2);
472 for (i = 0; i < numpict; i++)
478 unsigned long off, sz;
481 ptr = beg = ref + *((unsigned long*)ref + 1 + i);
486 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
487 if (!bi) return FALSE;
489 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
490 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
491 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
492 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
493 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
494 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
495 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
496 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
497 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
498 bi->bmiHeader.biCompression = BI_RGB;
499 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
500 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
501 shift = 32 / bi->bmiHeader.biBitCount;
502 bi->bmiHeader.biSizeImage = ((bi->bmiHeader.biWidth + shift - 1) / shift) * 4 * bi->bmiHeader.biHeight;
504 sz = fetch_ulong(&ptr);
505 fetch_ulong(&ptr); /* hotspot size */
507 off = *(unsigned long*)ptr; ptr += 4;
508 /* *(unsigned long*)ptr; hotspot offset */ ptr += 4;
510 /* now read palette info */
513 unsigned nc = bi->bmiHeader.biClrUsed;
516 if (!nc) nc = 1 << bi->bmiHeader.biBitCount;
517 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
518 if (!bi) return FALSE;
519 for (i = 0; i < nc; i++)
521 bi->bmiColors[i].rgbBlue = ptr[0];
522 bi->bmiColors[i].rgbGreen = ptr[1];
523 bi->bmiColors[i].rgbRed = ptr[2];
524 bi->bmiColors[i].rgbReserved = 0;
531 case 0: /* uncompressed */
532 pict_beg = beg + off;
533 if (sz != bi->bmiHeader.biSizeImage)
534 WINE_WARN("Bogus image sizes: %lu / %lu [sz=(%lu,%lu) bc=%u pl=%u]\n",
535 sz, bi->bmiHeader.biSizeImage,
536 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight,
537 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes);
543 dst = pict_beg = HeapAlloc(GetProcessHeap(), 0, bi->bmiHeader.biSizeImage);
544 if (!pict_beg) return FALSE;
545 HLPFILE_UncompressRLE(beg + off, sz, &dst);
546 if (dst - pict_beg != bi->bmiHeader.biSizeImage)
547 WINE_FIXME("buffer XXX-flow\n");
553 esz = HLPFILE_UncompressedLZ77_Size(beg + off, beg + off + sz);
554 pict_beg = HeapAlloc(GetProcessHeap(), 0, esz);
555 if (!pict_beg) return FALSE;
556 HLPFILE_UncompressLZ77(beg + off, beg + off + sz, pict_beg);
557 if (esz != bi->bmiHeader.biSizeImage)
558 WINE_WARN("Bogus image sizes: %lu / %lu [sz=(%lu,%lu) bc=%u pl=%u]\n",
559 esz, bi->bmiHeader.biSizeImage,
560 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight,
561 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes);
564 case 3: /* LZ77 then RLE */
570 sz77 = HLPFILE_UncompressedLZ77_Size(beg + off, beg + off + sz);
571 tmp = HeapAlloc(GetProcessHeap(), 0, bi->bmiHeader.biSizeImage);
572 if (!tmp) return FALSE;
573 HLPFILE_UncompressLZ77(beg + off, beg + off + sz, tmp);
574 pict_beg = dst = HeapAlloc(GetProcessHeap(), 0, bi->bmiHeader.biSizeImage);
575 if (!pict_beg) return FALSE;
576 HLPFILE_UncompressRLE(tmp, sz77, &dst);
577 if (dst - pict_beg != bi->bmiHeader.biSizeImage)
578 WINE_WARN("Bogus image sizes: %u / %lu [sz=(%lu,%lu) bc=%u pl=%u]\n",
579 dst - pict_beg, bi->bmiHeader.biSizeImage,
580 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight,
581 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes);
582 HeapFree(GetProcessHeap(), 0, tmp);
586 WINE_FIXME("Unsupported packing %u\n", pack);
590 attributes.hBitmap = CreateDIBitmap(GetDC(0), &bi->bmiHeader, CBM_INIT,
591 pict_beg, bi, DIB_RGB_COLORS);
592 if (!attributes.hBitmap)
593 WINE_ERR("Couldn't create bitmap\n");
594 attributes.bmpPos = pos;
596 HeapFree(GetProcessHeap(), 0, bi);
597 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
599 /* FIXME: implement support for multiple picture format */
600 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
606 /******************************************************************
607 * HLPFILE_LoadPictureByIndex
611 static BOOL HLPFILE_LoadPictureByIndex(HLPFILE *hlpfile, unsigned index, unsigned pos)
616 WINE_TRACE("Loading picture #%d\n", index);
617 sprintf(tmp, "bm%u", index);
619 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
623 return HLPFILE_LoadPictureByAddr(hlpfile, ref, end - ref, pos);
626 /***********************************************************************
628 * HLPFILE_AddParagraph
630 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
633 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
635 BYTE *format, *format_end, *text, *text_end;
638 unsigned nc, ncol = 1;
640 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
642 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
643 for (paragraphptr = &page->first_paragraph; *paragraphptr;
644 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
646 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
648 size = GET_UINT(buf, 0x4);
650 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
652 if (hlpfile->hasPhrases)
654 BYTE* lptr = buf + GET_UINT(buf, 0x10);
657 size2 = HLPFILE_Uncompressed2_Size(lptr, end);
658 if (size2 != size + 1)
659 WINE_FIXME("Mismatch in sizes: decomp2=%u header=%lu\n", size2, size);
660 text = HeapAlloc(GetProcessHeap(), 0, size + 1);
661 if (!text) return FALSE;
662 HLPFILE_Uncompress2(&lptr, end, text);
666 /* block is compressed */
667 text = HeapAlloc(GetProcessHeap(), 0, size);
668 if (!text) return FALSE;
669 HLPFILE_Uncompress3(text, text + size, buf + GET_UINT(buf, 0x10), end);
674 text = buf + GET_UINT(buf, 0x10);
676 text_end = text + size;
679 format_end = buf + GET_UINT(buf, 0x10);
682 *len = fetch_ushort(&format);
683 if (buf[0x14] == 0x23)
689 WINE_TRACE("#cols %u\n", ncol);
691 if (type == 0 || type == 2)
696 for (nc = 0; nc < ncol; nc++)
698 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
699 if (buf[0x14] == 0x23)
702 bits = *(unsigned short*)format; format += 2;
703 if (bits & 0x0001) fetch_long(&format);
704 if (bits & 0x0002) fetch_short(&format);
705 if (bits & 0x0004) fetch_short(&format);
706 if (bits & 0x0008) fetch_short(&format);
707 if (bits & 0x0010) fetch_short(&format);
708 if (bits & 0x0020) fetch_short(&format);
709 if (bits & 0x0040) fetch_short(&format);
710 if (bits & 0x0100) format += 3;
713 int ntab = fetch_short(&format);
718 ts = fetch_ushort(&format);
719 if (ts & 0x4000) fetch_ushort(&format);
723 while (text < text_end && format < format_end)
725 WINE_TRACE("Got text: '%s' (%p/%p - %p/%p)\n", text, text, text_end, format, format_end);
726 textsize = strlen(text) + 1;
727 if (textsize > 1 || attributes.hBitmap)
729 paragraph = HeapAlloc(GetProcessHeap(), 0,
730 sizeof(HLPFILE_PARAGRAPH) + textsize);
731 if (!paragraph) return FALSE;
732 *paragraphptr = paragraph;
733 paragraphptr = ¶graph->next;
735 paragraph->next = NULL;
736 paragraph->link = NULL;
738 if (attributes.hBitmap)
740 paragraph->cookie = para_image;
741 paragraph->u.image.hBitmap = attributes.hBitmap;
742 paragraph->u.image.pos = attributes.bmpPos;
743 if (attributes.wVSpace) paragraph->u.image.pos |= 0x8000;
747 paragraph->cookie = (attributes.bDebug) ? para_debug_text : para_normal_text;
748 paragraph->u.text.wFont = attributes.wFont;
749 paragraph->u.text.wVSpace = attributes.wVSpace;
750 paragraph->u.text.wHSpace = attributes.wHSpace;
751 paragraph->u.text.wIndent = attributes.wIndent;
752 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
753 strcpy(paragraph->u.text.lpszText, text);
756 if (attributes.link.lpszPath)
758 /* FIXME: should build a string table for the attributes.link.lpszPath
759 * they are reallocated for each link
761 paragraph->link = HeapAlloc(GetProcessHeap(), 0,
762 sizeof(HLPFILE_LINK) + strlen(attributes.link.lpszPath) + 1);
763 if (!paragraph->link) return FALSE;
765 paragraph->link->lpszPath = (char*)paragraph->link + sizeof(HLPFILE_LINK);
766 strcpy((char*)paragraph->link->lpszPath, attributes.link.lpszPath);
767 paragraph->link->lHash = attributes.link.lHash;
769 paragraph->link->bPopup = attributes.link.bPopup;
770 WINE_TRACE("Link to %s/%08lx\n",
771 paragraph->link->lpszPath, paragraph->link->lHash);
774 memset(&attributes, 0, sizeof(attributes));
776 attributes.hBitmap = 0;
777 attributes.link.lpszPath = NULL;
778 attributes.wVSpace = 0;
779 attributes.wHSpace = 0;
780 attributes.wIndent = 0;
783 /* else: null text, keep on storing attributes */
792 WINE_TRACE("format=%02x\n", *format);
806 attributes.wFont = GET_USHORT(format, 1);
807 WINE_TRACE("Changing font to %d\n", attributes.wFont);
812 attributes.wVSpace++;
817 attributes.wVSpace += 2 - attributes.wVBackSpace;
818 attributes.wVBackSpace = 0;
819 attributes.wIndent = 0;
824 attributes.wIndent++;
838 BYTE pos = (*format - 0x86);
839 BYTE type = format[1];
843 size = fetch_long(&format);
847 fetch_ushort(&format); /* hot spot */
850 if (*(short*)format == 0)
851 HLPFILE_LoadPictureByIndex(hlpfile,
852 *(short*)(format + 2),
856 WINE_FIXME("does it work ???\n");
857 HLPFILE_LoadPictureByAddr(hlpfile, format + 2,
862 WINE_FIXME("Got an embedded element %s\n", format + 6);
865 WINE_FIXME("Got a type %d picture\n", type);
873 attributes.wVBackSpace++;
891 WINE_FIXME("macro NIY %s\n", format + 3);
892 format += GET_USHORT(format, 1) + 3;
897 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
903 attributes.link.lpszPath = hlpfile->lpszPath;
904 attributes.link.lHash = GET_UINT(format, 1);
905 attributes.link.bPopup = !(*format & 1);
911 WINE_WARN("jump topic 2 => %u\n", GET_UINT(format, 1));
916 attributes.link.lpszPath = format + 8;
917 attributes.link.lHash = GET_UINT(format, 4);
918 attributes.link.bPopup = !(*format & 1);
919 format += 3 + GET_USHORT(format, 1);
925 WINE_WARN("jump to external file\n");
926 format += 3 + GET_USHORT(format, 1);
930 WINE_WARN("format %02x\n", *format);
935 if (text_end != buf + GET_UINT(buf, 0x10) + size)
936 HeapFree(GetProcessHeap(), 0, text_end - size);
940 /******************************************************************
945 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
948 unsigned i, len, idx;
949 unsigned face_num, dscr_num, face_offset, dscr_offset;
952 if (!HLPFILE_FindSubFile("FONT", &ref, &end))
954 WINE_WARN("no subfile FONT\n");
955 hlpfile->numFonts = 0;
956 hlpfile->fonts = NULL;
962 face_num = GET_USHORT(ref, 0);
963 dscr_num = GET_USHORT(ref, 2);
964 face_offset = GET_USHORT(ref, 4);
965 dscr_offset = GET_USHORT(ref, 6);
967 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
968 face_num, face_offset, dscr_num, dscr_offset);
970 hlpfile->numFonts = dscr_num;
971 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
973 len = (dscr_offset - face_offset) / face_num;
974 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
975 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
976 for (i = 0; i < dscr_num; i++)
978 flag = ref[dscr_offset + i * 11 + 0];
979 family = ref[dscr_offset + i * 11 + 2];
981 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2;
982 hlpfile->fonts[i].LogFont.lfWidth = 0;
983 hlpfile->fonts[i].LogFont.lfEscapement = 0;
984 hlpfile->fonts[i].LogFont.lfOrientation = 0;
985 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
986 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
987 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
988 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
989 hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
990 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
991 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
992 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
993 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
997 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
998 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
999 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1000 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1001 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1002 default: WINE_FIXME("Unknown family %u\n", family);
1004 idx = *(unsigned short*)(ref + dscr_offset + i * 11 + 3);
1008 strncpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1009 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1) + 1] = '\0';
1013 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1014 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1016 hlpfile->fonts[i].hFont = (HANDLE)0;
1017 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1018 ref[dscr_offset + i * 11 + 6],
1019 ref[dscr_offset + i * 11 + 7]);
1020 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1021 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08lx\n",
1027 X(4, "dblUnderline"),
1029 ref[dscr_offset + i * 11 + 1],
1031 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1032 *(unsigned long*)(ref + dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1035 --- only if FacenamesOffset >= 12
1036 unsigned short NumStyles number of style descriptors
1037 unsigned short StyleOffset start of array of style descriptors
1038 relative to &NumFacenames
1039 --- only if FacenamesOffset >= 16
1040 unsigned short NumCharMapTables number of character mapping tables
1041 unsigned short CharMapTableOffset start of array of character mapping
1042 table names relative to &NumFacenames
1047 /***********************************************************************
1049 * HLPFILE_ReadFileToBuffer
1051 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1053 BYTE header[16], dummy[1];
1056 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1058 size = GET_UINT(header, 12);
1059 file_buffer = HeapAlloc(GetProcessHeap(), 0, size + 1);
1060 if (!file_buffer) return FALSE;
1062 memcpy(file_buffer, header, 16);
1063 if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
1064 {WINE_WARN("filesize1\n"); return FALSE;};
1066 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1068 file_buffer[size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1073 /***********************************************************************
1075 * HLPFILE_FindSubFile
1077 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1079 BYTE *root = file_buffer + GET_UINT(file_buffer, 4);
1080 BYTE *end = file_buffer + GET_UINT(file_buffer, 12);
1081 BYTE *ptr = root + 0x37;
1083 while (ptr < end && ptr[0] == 0x7c)
1085 BYTE *fname = ptr + 1;
1086 ptr += strlen(ptr) + 1;
1087 if (!lstrcmpi(fname, name))
1089 *subbuf = file_buffer + GET_UINT(ptr, 0);
1090 *subend = *subbuf + GET_UINT(*subbuf, 0);
1091 if (file_buffer > *subbuf || *subbuf > *subend || *subend > end)
1093 WINE_WARN("size mismatch\n");
1103 /***********************************************************************
1105 * HLPFILE_SystemCommands
1107 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1109 BYTE *buf, *ptr, *end;
1110 HLPFILE_MACRO *macro, **m;
1112 unsigned short magic, minor, major, flags;
1114 hlpfile->lpszTitle = NULL;
1116 if (!HLPFILE_FindSubFile("SYSTEM", &buf, &end)) return FALSE;
1118 magic = GET_USHORT(buf + 9, 0);
1119 minor = GET_USHORT(buf + 9, 2);
1120 major = GET_USHORT(buf + 9, 4);
1121 /* gen date on 4 bytes */
1122 flags = GET_USHORT(buf + 9, 10);
1123 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1124 magic, major, minor, flags);
1125 if (magic != 0x036C || major != 1)
1126 {WINE_WARN("Wrong system header\n"); return FALSE;}
1127 if (minor <= 16) {WINE_WARN("too old file format (NIY)\n"); return FALSE;}
1129 hlpfile->version = minor;
1130 hlpfile->flags = flags;
1132 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1134 switch (GET_USHORT(ptr, 0))
1137 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1138 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(ptr + 4) + 1);
1139 if (!hlpfile->lpszTitle) return FALSE;
1140 lstrcpy(hlpfile->lpszTitle, ptr + 4);
1141 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1145 if (GET_USHORT(ptr, 2) != 1 || ptr[4] != 0) WINE_WARN("system2\n");
1149 if (GET_USHORT(ptr, 2) != 4 || GET_UINT(ptr, 4) != 0)
1150 WINE_WARN("system3\n");
1154 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1);
1156 p = (char*)macro + sizeof(HLPFILE_MACRO);
1157 lstrcpy(p, (LPSTR)ptr + 4);
1158 macro->lpszMacro = p;
1160 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1165 WINE_WARN("Unsupport SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1171 /***********************************************************************
1173 * HLPFILE_UncompressedLZ77_Size
1175 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1182 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1186 int code = GET_USHORT(ptr, 0);
1187 int len = 3 + (code >> 12);
1191 else newsize++, ptr++;
1198 /***********************************************************************
1200 * HLPFILE_UncompressLZ77
1202 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1209 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1213 int code = GET_USHORT(ptr, 0);
1214 int len = 3 + (code >> 12);
1215 int offset = code & 0xfff;
1216 memcpy(newptr, newptr - offset - 1, len);
1220 else *newptr++ = *ptr++;
1227 /***********************************************************************
1229 * HLPFILE_UncompressLZ77_Phrases
1231 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1233 UINT i, num, dec_size;
1236 if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) return FALSE;
1238 num = phrases.num = GET_USHORT(buf, 9);
1239 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1241 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1243 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1244 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1245 if (!phrases.offsets || !phrases.buffer) return FALSE;
1247 for (i = 0; i <= num; i++)
1248 phrases.offsets[i] = GET_USHORT(buf, 0x11 + 2 * i) - 2 * num - 2;
1250 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, phrases.buffer);
1252 hlpfile->hasPhrases = TRUE;
1256 /***********************************************************************
1258 * HLPFILE_Uncompress_Phrases40
1260 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1263 BYTE *buf_idx, *end_idx;
1264 BYTE *buf_phs, *end_phs;
1266 long* ptr, mask = 0;
1269 if (!HLPFILE_FindSubFile("PhrIndex", &buf_idx, &end_idx) ||
1270 !HLPFILE_FindSubFile("PhrImage", &buf_phs, &end_phs)) return FALSE;
1272 ptr = (long*)(buf_idx + 9 + 28);
1273 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1274 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1276 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1277 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1278 GET_UINT(buf_idx, 9 + 0),
1279 GET_UINT(buf_idx, 9 + 4),
1280 GET_UINT(buf_idx, 9 + 8),
1281 GET_UINT(buf_idx, 9 + 12),
1282 GET_UINT(buf_idx, 9 + 16),
1283 GET_UINT(buf_idx, 9 + 20),
1284 GET_USHORT(buf_idx, 9 + 24),
1285 GET_USHORT(buf_idx, 9 + 26));
1287 dec_size = GET_UINT(buf_idx, 9 + 12);
1288 if (dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1290 WINE_WARN("size mismatch %u %u\n",
1291 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs, end_phs));
1292 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs, end_phs));
1295 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1296 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1297 if (!phrases.offsets || !phrases.buffer) return FALSE;
1299 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1301 phrases.offsets[0] = 0;
1302 for (i = 0; i < num; i++)
1304 for (n = 1; getbit(); n += 1 << bc);
1306 if (bc > 1 && getbit()) n += 2;
1307 if (bc > 2 && getbit()) n += 4;
1308 if (bc > 3 && getbit()) n += 8;
1309 if (bc > 4 && getbit()) n += 16;
1310 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1314 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, phrases.buffer);
1316 hlpfile->hasPhrases = FALSE;
1320 /***********************************************************************
1322 * HLPFILE_UncompressLZ77_Topic
1324 static BOOL HLPFILE_UncompressLZ77_Topic(HLPFILE* hlpfile)
1326 BYTE *buf, *ptr, *end, *newptr;
1329 if (!HLPFILE_FindSubFile("TOPIC", &buf, &end))
1330 {WINE_WARN("topic0\n"); return FALSE;}
1332 if (!(hlpfile->flags & 4)) WINE_FIXME("Unsupported format\n");
1335 topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
1337 for (i = 0; i < topic.wMapLen; i++)
1339 ptr = buf + i * 0x1000;
1341 /* I don't know why, it's necessary for printman.hlp */
1342 if (ptr + 0x44 > end) ptr = end - 0x44;
1344 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + 0x1000));
1347 topic.map = HeapAlloc(GetProcessHeap(), 0,
1348 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1349 if (!topic.map) return FALSE;
1350 newptr = (char*)topic.map + topic.wMapLen * sizeof(topic.map[0]);
1351 topic.end = newptr + newsize;
1353 for (i = 0; i < topic.wMapLen; i++)
1355 ptr = buf + i * 0x1000;
1356 if (ptr + 0x44 > end) ptr = end - 0x44;
1358 topic.map[i] = newptr;
1359 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + 0x1000), newptr);
1365 /***********************************************************************
1367 * HLPFILE_Uncompressed2_Size
1369 static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end)
1375 if (!*ptr || *ptr >= 0x10)
1379 BYTE *phptr, *phend;
1380 UINT code = 0x100 * ptr[0] + ptr[1];
1381 UINT index = (code - 0x100) / 2;
1383 if (index < phrases.num)
1385 phptr = phrases.buffer + phrases.offsets[index];
1386 phend = phrases.buffer + phrases.offsets[index + 1];
1388 if (phend < phptr) WINE_WARN("uncompress2a\n");
1390 wSize += phend - phptr;
1391 if (code & 1) wSize++;
1393 else WINE_WARN("uncompress2b %d|%d\n", index, phrases.num);
1402 /***********************************************************************
1404 * HLPFILE_Uncompress2
1407 static void HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr)
1413 if (!*ptr || *ptr >= 0x10)
1417 BYTE *phptr, *phend;
1418 UINT code = 0x100 * ptr[0] + ptr[1];
1419 UINT index = (code - 0x100) / 2;
1421 phptr = phrases.buffer + phrases.offsets[index];
1422 phend = phrases.buffer + phrases.offsets[index + 1];
1424 memcpy(newptr, phptr, phend - phptr);
1425 newptr += phend - phptr;
1426 if (code & 1) *newptr++ = ' ';
1435 /******************************************************************
1436 * HLPFILE_Uncompress3
1440 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1441 const BYTE* src, const BYTE* src_end)
1445 for (; src < src_end; src++)
1447 if ((*src & 1) == 0)
1450 if (idx > phrases.num) WINE_ERR("index in phrases\n");
1451 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1452 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1454 else if ((*src & 0x03) == 0x01)
1456 idx = (*src + 1) * 64 + *++src;
1457 if (idx > phrases.num) WINE_ERR("index in phrases\n");
1458 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1459 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1461 else if ((*src & 0x07) == 0x03)
1463 len = (*src / 8) + 1;
1464 memcpy(dst, src + 1, len);
1469 len = (*src / 16) + 1;
1470 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1475 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1479 /******************************************************************
1480 * HLPFILE_UncompressRLE
1484 static void HLPFILE_UncompressRLE(const BYTE* src, unsigned sz, BYTE** dst)
1489 for (i = 0; i < sz; i++)
1495 memcpy(*dst, src + i + 1, ch);
1500 memset(*dst, (char)src[i + 1], ch);
1507 /******************************************************************
1508 * HLPFILE_EnumBTreeLeaves
1512 static void HLPFILE_EnumBTreeLeaves(const BYTE* buf, const BYTE* end, unsigned (*fn)(const BYTE*, void*), void* user)
1514 unsigned psize, pnext;
1518 num = GET_UINT(buf, 9 + 34);
1519 psize = GET_USHORT(buf, 9 + 4);
1520 nlvl = GET_USHORT(buf, 9 + 32);
1521 pnext = GET_USHORT(buf, 26);
1523 WINE_TRACE("BTree: #entries=%u pagSize=%u #levels=%u #pages=%u root=%u struct%16s\n",
1524 num, psize, nlvl, GET_USHORT(buf, 9 + 30), pnext, buf + 9 + 6);
1529 ptr = (buf + 9 + 38) + pnext * psize;
1530 WINE_TRACE("BTree: (index[%u]) unused=%u #entries=%u <%u\n",
1531 pnext, GET_USHORT(ptr, 0), GET_USHORT(ptr, 2), GET_USHORT(ptr, 4));
1532 pnext = GET_USHORT(ptr, 6);
1534 while (pnext != 0xFFFF)
1536 const BYTE* node_page;
1537 unsigned short limit;
1539 node_page = ptr = (buf + 9 + 38) + pnext * psize;
1540 limit = GET_USHORT(ptr, 2);
1541 WINE_TRACE("BTree: (leaf [%u]) unused=%u #entries=%u <%u >%u\n",
1542 pnext, GET_USHORT(ptr, 0), limit, GET_USHORT(ptr, 4), GET_USHORT(ptr, 6));
1545 ptr += (fn)(ptr, user);
1546 pnext = GET_USHORT(node_page, 6);
1555 static unsigned myfn(const BYTE* ptr, void* user)
1557 struct myfncb* m = user;
1559 m->hlpfile->Context[m->i].lHash = GET_UINT(ptr, 0);
1560 m->hlpfile->Context[m->i].offset = GET_UINT(ptr, 4);
1565 /***********************************************************************
1567 * HLPFILE_GetContext
1569 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
1575 if (!HLPFILE_FindSubFile("CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
1577 clen = GET_UINT(cbuf, 0x2b);
1578 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen * sizeof(HLPFILE_CONTEXT));
1579 if (!hlpfile->Context) return FALSE;
1580 hlpfile->wContextLen = clen;
1582 m.hlpfile = hlpfile;
1584 HLPFILE_EnumBTreeLeaves(cbuf, cend, myfn, &m);
1589 /***********************************************************************
1591 * HLPFILE_DeleteParagraph
1593 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
1595 HLPFILE_PARAGRAPH* next;
1599 next = paragraph->next;
1600 if (paragraph->link) HeapFree(GetProcessHeap(), 0, paragraph->link);
1602 HeapFree(GetProcessHeap(), 0, paragraph);
1607 /***********************************************************************
1611 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
1618 HLPFILE_DeleteParagraph(page->first_paragraph);
1619 HeapFree(GetProcessHeap(), 0, page);
1624 /***********************************************************************
1628 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
1630 HLPFILE_MACRO* next;
1635 HeapFree(GetProcessHeap(), 0, macro);
1640 /***********************************************************************
1642 * HLPFILE_FreeHlpFile
1644 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
1646 if (!hlpfile || --hlpfile->wRefCount > 0) return;
1648 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
1649 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
1650 else first_hlpfile = hlpfile->next;
1652 if (hlpfile->numFonts)
1655 for (i = 0; i < hlpfile->numFonts; i++)
1657 DeleteObject(hlpfile->fonts[i].hFont);
1659 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
1662 HLPFILE_DeletePage(hlpfile->first_page);
1663 HLPFILE_DeleteMacro(hlpfile->first_macro);
1665 if (hlpfile->Context) HeapFree(GetProcessHeap(), 0, hlpfile->Context);
1666 if (hlpfile->lpszTitle) HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
1667 HeapFree(GetProcessHeap(), 0, hlpfile);
1670 /***********************************************************************
1674 void HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page)
1677 HLPFILE_FreeHlpFile(page->file);