libwine: Emulate MS linker stub for builtin dlls.
[wine/wine64.git] / programs / winhelp / hlpfile.c
blob417148e7ce174f6b183d70a6927976cf77535e5b
1 /*
2 * Help Viewer
4 * Copyright 1996 Ulrich Schmid
5 * 2002 Eric Pouech
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winhelp.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
36 static inline unsigned short GET_USHORT(const BYTE* buffer, unsigned i)
38 return (BYTE)buffer[i] + 0x100 * (BYTE)buffer[i + 1];
41 static inline short GET_SHORT(const BYTE* buffer, unsigned i)
43 return (BYTE)buffer[i] + 0x100 * (signed char)buffer[i+1];
46 static inline unsigned GET_UINT(const BYTE* buffer, unsigned i)
48 return GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i + 2);
51 static HLPFILE *first_hlpfile = 0;
52 static BYTE *file_buffer;
54 static struct
56 UINT num;
57 unsigned* offsets;
58 char* buffer;
59 } phrases;
61 static struct
63 BYTE** map;
64 BYTE* end;
65 UINT wMapLen;
66 } topic;
68 static struct
70 UINT wFont;
71 UINT wIndent;
72 UINT wHSpace;
73 UINT wVSpace;
74 HLPFILE_LINK* link;
75 } attributes;
77 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
78 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
79 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
80 static BOOL HLPFILE_SystemCommands(HLPFILE*);
81 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end);
82 static BYTE* HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr);
83 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE*);
84 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE*);
85 static BOOL HLPFILE_Uncompress_Topic(HLPFILE*);
86 static BOOL HLPFILE_GetContext(HLPFILE*);
87 static BOOL HLPFILE_GetMap(HLPFILE*);
88 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
89 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
90 static void HLPFILE_Uncompress2(const BYTE*, const BYTE*, BYTE*, const BYTE*);
91 static BOOL HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
92 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz);
93 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile);
95 #if 0
96 /***********************************************************************
98 * HLPFILE_PageByNumber
100 static HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
102 HLPFILE_PAGE *page;
103 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
105 if (!hlpfile) return 0;
107 WINE_TRACE("[%s/%u]\n", lpszPath, wNum);
109 for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
111 /* HLPFILE_FreeHlpFile(lpszPath); */
113 return page;
115 #endif
117 /* FIXME:
118 * this finds the page containing the offset. The offset can either
119 * refer to the top of the page (offset == page->offset), or
120 * to some paragraph inside the page...
121 * As of today, we only return the page... we should also return
122 * a paragraph, and then, while opening a new page, compute the
123 * y-offset of the paragraph to be shown and scroll the window
124 * accordinly
126 /******************************************************************
127 * HLPFILE_PageByOffset
131 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
133 HLPFILE_PAGE* page;
134 HLPFILE_PAGE* found;
136 if (!hlpfile) return 0;
138 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, offset);
140 if (offset == 0xFFFFFFFF) return NULL;
141 page = NULL;
143 for (found = NULL, page = hlpfile->first_page; page; page = page->next)
145 if (page->offset <= offset && (!found || found->offset < page->offset))
146 found = page;
148 if (!found)
149 WINE_ERR("Page of offset %u not found in file %s\n",
150 offset, hlpfile->lpszPath);
151 return found;
154 /***********************************************************************
156 * HLPFILE_HlpFilePageByHash
158 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
160 unsigned int i;
162 if (!hlpfile) return 0;
164 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lHash);
166 for (i = 0; i < hlpfile->wContextLen; i++)
168 if (hlpfile->Context[i].lHash == lHash)
169 return HLPFILE_PageByOffset(hlpfile, hlpfile->Context[i].offset);
172 WINE_ERR("Page of hash %x not found in file %s\n", lHash, hlpfile->lpszPath);
173 return NULL;
176 /***********************************************************************
178 * HLPFILE_PageByMap
180 HLPFILE_PAGE *HLPFILE_PageByMap(HLPFILE* hlpfile, LONG lMap)
182 unsigned int i;
184 if (!hlpfile) return 0;
186 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lMap);
188 for (i = 0; i < hlpfile->wMapLen; i++)
190 if (hlpfile->Map[i].lMap == lMap)
191 return HLPFILE_PageByOffset(hlpfile, hlpfile->Map[i].offset);
194 WINE_ERR("Page of Map %x not found in file %s\n", lMap, hlpfile->lpszPath);
195 return NULL;
198 /***********************************************************************
200 * HLPFILE_Contents
202 HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
204 HLPFILE_PAGE* page = NULL;
206 if (!hlpfile) return NULL;
208 page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
209 if (!page) page = hlpfile->first_page;
210 return page;
213 /***********************************************************************
215 * HLPFILE_Hash
217 LONG HLPFILE_Hash(LPCSTR lpszContext)
219 LONG lHash = 0;
220 CHAR c;
222 while ((c = *lpszContext++))
224 CHAR x = 0;
225 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
226 if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
227 if (c >= '1' && c <= '9') x = c - '0';
228 if (c == '0') x = 10;
229 if (c == '.') x = 12;
230 if (c == '_') x = 13;
231 if (x) lHash = lHash * 43 + x;
233 return lHash;
236 /***********************************************************************
238 * HLPFILE_ReadHlpFile
240 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
242 HLPFILE* hlpfile;
244 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
246 if (!strcmp(lpszPath, hlpfile->lpszPath))
248 hlpfile->wRefCount++;
249 return hlpfile;
253 hlpfile = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
254 if (!hlpfile) return 0;
256 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
257 hlpfile->lpszTitle = NULL;
258 hlpfile->lpszCopyright = NULL;
259 hlpfile->first_page = NULL;
260 hlpfile->first_macro = NULL;
261 hlpfile->wContextLen = 0;
262 hlpfile->Context = NULL;
263 hlpfile->wMapLen = 0;
264 hlpfile->Map = NULL;
265 hlpfile->contents_start = 0xFFFFFFFF;
266 hlpfile->prev = NULL;
267 hlpfile->next = first_hlpfile;
268 hlpfile->wRefCount = 1;
270 hlpfile->numBmps = 0;
271 hlpfile->bmps = NULL;
273 hlpfile->numFonts = 0;
274 hlpfile->fonts = NULL;
276 hlpfile->numWindows = 0;
277 hlpfile->windows = NULL;
279 strcpy(hlpfile->lpszPath, lpszPath);
281 first_hlpfile = hlpfile;
282 if (hlpfile->next) hlpfile->next->prev = hlpfile;
284 phrases.offsets = NULL;
285 phrases.buffer = NULL;
286 topic.map = NULL;
287 topic.end = NULL;
288 file_buffer = NULL;
290 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
292 HLPFILE_FreeHlpFile(hlpfile);
293 hlpfile = 0;
296 HeapFree(GetProcessHeap(), 0, phrases.offsets);
297 HeapFree(GetProcessHeap(), 0, phrases.buffer);
298 HeapFree(GetProcessHeap(), 0, topic.map);
299 HeapFree(GetProcessHeap(), 0, file_buffer);
301 return hlpfile;
304 /***********************************************************************
306 * HLPFILE_DoReadHlpFile
308 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
310 BOOL ret;
311 HFILE hFile;
312 OFSTRUCT ofs;
313 BYTE* buf;
314 DWORD ref = 0x0C;
315 unsigned index, old_index, offset, len, offs;
317 hFile = OpenFile(lpszPath, &ofs, OF_READ);
318 if (hFile == HFILE_ERROR) return FALSE;
320 ret = HLPFILE_ReadFileToBuffer(hFile);
321 _lclose(hFile);
322 if (!ret) return FALSE;
324 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
326 /* load phrases support */
327 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
328 HLPFILE_Uncompress_Phrases40(hlpfile);
330 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
331 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
333 buf = topic.map[0];
334 old_index = -1;
335 offs = 0;
338 BYTE* end;
340 /* FIXME this depends on the blocksize, can be 2k in some cases */
341 index = (ref - 0x0C) >> 14;
342 offset = (ref - 0x0C) & 0x3fff;
344 WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset);
346 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
347 buf = topic.map[index] + offset;
348 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
349 end = min(buf + GET_UINT(buf, 0), topic.end);
350 if (index != old_index) {offs = 0; old_index = index;}
352 switch (buf[0x14])
354 case 0x02:
355 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
356 break;
358 case 0x20:
359 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
360 offs += len;
361 break;
363 case 0x23:
364 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
365 offs += len;
366 break;
368 default:
369 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
372 ref = GET_UINT(buf, 0xc);
373 } while (ref != 0xffffffff);
375 HLPFILE_GetMap(hlpfile);
376 return HLPFILE_GetContext(hlpfile);
379 /***********************************************************************
381 * HLPFILE_AddPage
383 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
385 HLPFILE_PAGE* page;
386 BYTE* title;
387 UINT titlesize;
388 char* ptr;
389 HLPFILE_MACRO*macro;
391 if (buf + 0x31 > end) {WINE_WARN("page1\n"); return FALSE;};
392 title = buf + GET_UINT(buf, 0x10);
393 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
395 titlesize = GET_UINT(buf, 4);
396 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
397 if (!page) return FALSE;
398 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
400 if (hlpfile->hasPhrases)
402 HLPFILE_Uncompress2(title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize);
404 else
406 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
408 /* need to decompress */
409 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize,
410 title, end);
412 else
414 memcpy(page->lpszTitle, title, titlesize);
418 page->lpszTitle[titlesize] = '\0';
420 if (hlpfile->first_page)
422 HLPFILE_PAGE *p;
424 for (p = hlpfile->first_page; p->next; p = p->next);
425 page->prev = p;
426 p->next = page;
428 else
430 hlpfile->first_page = page;
431 page->prev = NULL;
434 page->file = hlpfile;
435 page->next = NULL;
436 page->first_paragraph = NULL;
437 page->first_macro = NULL;
438 page->wNumber = GET_UINT(buf, 0x21);
439 page->offset = offset;
441 page->browse_bwd = GET_UINT(buf, 0x19);
442 page->browse_fwd = GET_UINT(buf, 0x1D);
444 WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
445 page->wNumber, page->lpszTitle,
446 page->browse_bwd, page->offset, page->browse_fwd);
448 memset(&attributes, 0, sizeof(attributes));
450 /* now load macros */
451 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
452 while (ptr < page->lpszTitle + titlesize)
454 unsigned len = strlen(ptr);
455 char* macro_str;
457 WINE_TRACE("macro: %s\n", ptr);
458 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
459 macro->lpszMacro = macro_str = (char*)(macro + 1);
460 memcpy(macro_str, ptr, len + 1);
461 /* FIXME: shall we really link macro in reverse order ??
462 * may produce strange results when played at page opening
464 macro->next = page->first_macro;
465 page->first_macro = macro;
466 ptr += len + 1;
469 return TRUE;
472 static long fetch_long(BYTE** ptr)
474 long ret;
476 if (*(*ptr) & 1)
478 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
479 (*ptr) += 4;
481 else
483 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
484 (*ptr) += 2;
487 return ret;
490 static unsigned long fetch_ulong(BYTE** ptr)
492 unsigned long ret;
494 if (*(*ptr) & 1)
496 ret = *(unsigned long*)(*ptr) / 2;
497 (*ptr) += 4;
499 else
501 ret = *(unsigned short*)(*ptr) / 2;
502 (*ptr) += 2;
504 return ret;
507 static short fetch_short(BYTE** ptr)
509 short ret;
511 if (*(*ptr) & 1)
513 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
514 (*ptr) += 2;
516 else
518 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
519 (*ptr)++;
521 return ret;
524 static unsigned short fetch_ushort(BYTE** ptr)
526 unsigned short ret;
528 if (*(*ptr) & 1)
530 ret = *(unsigned short*)(*ptr) / 2;
531 (*ptr) += 2;
533 else
535 ret = *(unsigned char*)(*ptr) / 2;
536 (*ptr)++;
538 return ret;
541 /******************************************************************
542 * HLPFILE_DecompressGfx
544 * Decompress the data part of a bitmap or a metafile
546 static BYTE* HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE packing)
548 BYTE* dst;
549 BYTE* tmp;
550 BYTE* tmp2;
551 unsigned sz77;
553 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
555 switch (packing)
557 case 0: /* uncompressed */
558 if (sz != csz)
559 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
560 dst = src;
561 break;
562 case 1: /* RunLen */
563 tmp = dst = HeapAlloc(GetProcessHeap(), 0, sz);
564 if (!dst) return NULL;
565 HLPFILE_UncompressRLE(src, src + csz, &tmp, sz);
566 if (tmp - dst != sz)
567 WINE_WARN("Bogus gfx sizes (RunLen): %u/%u\n", tmp - dst, sz);
568 break;
569 case 2: /* LZ77 */
570 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
571 dst = HeapAlloc(GetProcessHeap(), 0, sz77);
572 if (!dst) return NULL;
573 HLPFILE_UncompressLZ77(src, src + csz, dst);
574 if (sz77 != sz)
575 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
576 break;
577 case 3: /* LZ77 then RLE */
578 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
579 tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
580 if (!tmp) return FALSE;
581 HLPFILE_UncompressLZ77(src, src + csz, tmp);
582 dst = tmp2 = HeapAlloc(GetProcessHeap(), 0, sz);
583 if (!dst) return FALSE;
584 HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
585 if (tmp2 - dst != sz)
586 WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
587 HeapFree(GetProcessHeap(), 0, tmp);
588 break;
589 default:
590 WINE_FIXME("Unsupported packing %u\n", packing);
591 return NULL;
593 return dst;
596 /******************************************************************
597 * HLPFILE_LoadBitmap
601 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack,
602 HLPFILE_PARAGRAPH* paragraph)
604 BYTE* ptr;
605 BYTE* pict_beg;
606 BITMAPINFO* bi;
607 unsigned long off, csz;
608 HDC hdc;
610 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
611 if (!bi) return FALSE;
613 ptr = beg + 2; /* for type and pack */
615 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
616 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
617 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
618 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
619 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
620 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
621 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
622 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
623 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
624 bi->bmiHeader.biCompression = BI_RGB;
625 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
626 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
627 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
628 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
629 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
630 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
632 csz = fetch_ulong(&ptr);
633 fetch_ulong(&ptr); /* hotspot size */
635 off = GET_UINT(ptr, 0); ptr += 4;
636 /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
638 /* now read palette info */
639 if (type == 0x06)
641 unsigned nc = bi->bmiHeader.biClrUsed;
642 unsigned i;
644 /* not quite right, especially for bitfields type of compression */
645 if (!nc && bi->bmiHeader.biBitCount <= 8)
646 nc = 1 << bi->bmiHeader.biBitCount;
648 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
649 if (!bi) return FALSE;
650 for (i = 0; i < nc; i++)
652 bi->bmiColors[i].rgbBlue = ptr[0];
653 bi->bmiColors[i].rgbGreen = ptr[1];
654 bi->bmiColors[i].rgbRed = ptr[2];
655 bi->bmiColors[i].rgbReserved = 0;
656 ptr += 4;
659 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
661 paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
662 CBM_INIT, pict_beg,
663 bi, DIB_RGB_COLORS);
664 ReleaseDC(0, hdc);
665 if (!paragraph->u.gfx.u.bmp.hBitmap)
666 WINE_ERR("Couldn't create bitmap\n");
668 HeapFree(GetProcessHeap(), 0, bi);
669 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
671 return TRUE;
674 /******************************************************************
675 * HLPFILE_LoadMetaFile
679 static BOOL HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
681 BYTE* ptr;
682 unsigned long size, csize;
683 unsigned long off, hsoff;
684 BYTE* bits;
685 LPMETAFILEPICT lpmfp;
687 WINE_TRACE("Loading metafile\n");
689 ptr = beg + 2; /* for type and pack */
691 lpmfp = &paragraph->u.gfx.u.mfp;
692 lpmfp->mm = fetch_ushort(&ptr); /* mapping mode */
694 lpmfp->xExt = GET_USHORT(ptr, 0);
695 lpmfp->yExt = GET_USHORT(ptr, 2);
696 ptr += 4;
698 size = fetch_ulong(&ptr); /* decompressed size */
699 csize = fetch_ulong(&ptr); /* compressed size */
700 fetch_ulong(&ptr); /* hotspot size */
701 off = GET_UINT(ptr, 0);
702 hsoff = GET_UINT(ptr, 4);
703 ptr += 8;
705 WINE_TRACE("sz=%lu csz=%lu (%d,%d) offs=%lu/%u,%lu\n",
706 size, csize, lpmfp->xExt, lpmfp->yExt, off, ptr - beg, hsoff);
708 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
709 if (!bits) return FALSE;
711 paragraph->cookie = para_metafile;
713 lpmfp->hMF = SetMetaFileBitsEx(size, bits);
715 if (!lpmfp->hMF)
716 WINE_FIXME("Couldn't load metafile\n");
718 if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
720 return TRUE;
723 /******************************************************************
724 * HLPFILE_LoadGfxByAddr
728 static BOOL HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
729 unsigned long size,
730 HLPFILE_PARAGRAPH* paragraph)
732 unsigned i, numpict;
734 numpict = GET_USHORT(ref, 2);
735 WINE_TRACE("Got picture magic=%04x #=%d\n",
736 GET_USHORT(ref, 0), numpict);
738 for (i = 0; i < numpict; i++)
740 BYTE* beg;
741 BYTE* ptr;
742 BYTE type, pack;
744 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
745 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
747 type = *ptr++;
748 pack = *ptr++;
750 switch (type)
752 case 5: /* device dependent bmp */
753 case 6: /* device independent bmp */
754 HLPFILE_LoadBitmap(beg, type, pack, paragraph);
755 break;
756 case 8:
757 HLPFILE_LoadMetaFile(beg, pack, paragraph);
758 break;
759 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
762 /* FIXME: hotspots */
764 /* FIXME: implement support for multiple picture format */
765 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
766 break;
768 return TRUE;
771 /******************************************************************
772 * HLPFILE_LoadGfxByIndex
776 static BOOL HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index,
777 HLPFILE_PARAGRAPH* paragraph)
779 char tmp[16];
780 BYTE *ref, *end;
781 BOOL ret;
783 WINE_TRACE("Loading picture #%d\n", index);
785 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
787 paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
788 return TRUE;
791 sprintf(tmp, "|bm%u", index);
793 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
795 ref += 9;
797 ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
799 /* cache bitmap */
800 if (ret && paragraph->cookie == para_bitmap)
802 if (index >= hlpfile->numBmps)
804 hlpfile->numBmps = index + 1;
805 if (hlpfile->bmps)
806 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
807 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
808 else
809 hlpfile->bmps = HeapAlloc(GetProcessHeap(), 0,
810 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
813 hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
815 return ret;
818 /******************************************************************
819 * HLPFILE_AllocLink
823 static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
824 BOOL clrChange, unsigned wnd)
826 HLPFILE_LINK* link;
827 char* link_str;
829 /* FIXME: should build a string table for the attributes.link.lpszPath
830 * they are reallocated for each link
832 link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
833 if (!link) return NULL;
835 link->cookie = cookie;
836 link->lpszString = link_str = (char*)link + sizeof(HLPFILE_LINK);
837 strcpy(link_str, str);
838 link->lHash = hash;
839 link->bClrChange = clrChange ? 1 : 0;
840 link->window = wnd;
841 link->wRefCount = 1;
843 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
844 link->cookie, link->lpszString,
845 link->lHash, link->window);
846 return link;
849 /***********************************************************************
851 * HLPFILE_AddParagraph
853 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
855 HLPFILE_PAGE *page;
856 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
857 UINT textsize;
858 BYTE *format, *format_end;
859 char *text, *text_end;
860 long size;
861 unsigned short bits;
862 unsigned nc, ncol = 1;
864 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
866 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
867 for (paragraphptr = &page->first_paragraph; *paragraphptr;
868 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
870 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
872 size = GET_UINT(buf, 0x4);
873 text = HeapAlloc(GetProcessHeap(), 0, size);
874 if (!text) return FALSE;
875 if (hlpfile->hasPhrases)
877 HLPFILE_Uncompress2(buf + GET_UINT(buf, 0x10), end, (BYTE*)text, (BYTE*)text + size);
879 else
881 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
883 /* block is compressed */
884 HLPFILE_Uncompress3(text, text + size, buf + GET_UINT(buf, 0x10), end);
886 else
888 text = (char*)buf + GET_UINT(buf, 0x10);
891 text_end = text + size;
893 format = buf + 0x15;
894 format_end = buf + GET_UINT(buf, 0x10);
896 fetch_long(&format);
897 *len = fetch_ushort(&format);
899 if (buf[0x14] == 0x23)
901 char type;
903 ncol = *format++;
905 WINE_TRACE("#cols %u\n", ncol);
906 type = *format++;
907 if (type == 0 || type == 2)
908 format += 2;
909 format += ncol * 4;
912 for (nc = 0; nc < ncol; nc++)
914 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
915 if (buf[0x14] == 0x23)
916 format += 5;
917 format += 4;
918 bits = GET_USHORT(format, 0); format += 2;
919 if (bits & 0x0001) fetch_long(&format);
920 if (bits & 0x0002) fetch_short(&format);
921 if (bits & 0x0004) fetch_short(&format);
922 if (bits & 0x0008) fetch_short(&format);
923 if (bits & 0x0010) fetch_short(&format);
924 if (bits & 0x0020) fetch_short(&format);
925 if (bits & 0x0040) fetch_short(&format);
926 if (bits & 0x0100) format += 3;
927 if (bits & 0x0200)
929 int ntab = fetch_short(&format);
930 unsigned short ts;
932 while (ntab-- > 0)
934 ts = fetch_ushort(&format);
935 if (ts & 0x4000) fetch_ushort(&format);
938 /* 0x0400, 0x0800 and 0x1000 don't need space */
939 if ((bits & 0xE080) != 0)
940 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
942 while (text < text_end && format < format_end)
944 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
945 textsize = strlen(text) + 1;
946 if (textsize > 1)
948 paragraph = HeapAlloc(GetProcessHeap(), 0,
949 sizeof(HLPFILE_PARAGRAPH) + textsize);
950 if (!paragraph) return FALSE;
951 *paragraphptr = paragraph;
952 paragraphptr = &paragraph->next;
954 paragraph->next = NULL;
955 paragraph->link = attributes.link;
956 if (paragraph->link) paragraph->link->wRefCount++;
957 paragraph->cookie = para_normal_text;
958 paragraph->u.text.wFont = attributes.wFont;
959 paragraph->u.text.wVSpace = attributes.wVSpace;
960 paragraph->u.text.wHSpace = attributes.wHSpace;
961 paragraph->u.text.wIndent = attributes.wIndent;
962 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
963 strcpy(paragraph->u.text.lpszText, text);
965 attributes.wVSpace = 0;
966 attributes.wHSpace = 0;
968 /* else: null text, keep on storing attributes */
969 text += textsize;
971 if (*format == 0xff)
973 format++;
974 break;
977 WINE_TRACE("format=%02x\n", *format);
978 switch (*format)
980 case 0x20:
981 WINE_FIXME("NIY20\n");
982 format += 5;
983 break;
985 case 0x21:
986 WINE_FIXME("NIY21\n");
987 format += 3;
988 break;
990 case 0x80:
991 attributes.wFont = GET_USHORT(format, 1);
992 WINE_TRACE("Changing font to %d\n", attributes.wFont);
993 format += 3;
994 break;
996 case 0x81:
997 attributes.wVSpace++;
998 format += 1;
999 break;
1001 case 0x82:
1002 attributes.wVSpace++;
1003 attributes.wIndent = 0;
1004 format += 1;
1005 break;
1007 case 0x83:
1008 attributes.wIndent++;
1009 format += 1;
1010 break;
1012 #if 0
1013 case 0x84:
1014 format += 3;
1015 break;
1016 #endif
1018 case 0x86:
1019 case 0x87:
1020 case 0x88:
1022 BYTE pos = (*format - 0x86);
1023 BYTE type = format[1];
1024 long size;
1026 format += 2;
1027 size = fetch_long(&format);
1029 paragraph = HeapAlloc(GetProcessHeap(), 0,
1030 sizeof(HLPFILE_PARAGRAPH) + textsize);
1031 if (!paragraph) return FALSE;
1032 *paragraphptr = paragraph;
1033 paragraphptr = &paragraph->next;
1035 paragraph->next = NULL;
1036 paragraph->link = attributes.link;
1037 if (paragraph->link) paragraph->link->wRefCount++;
1038 paragraph->cookie = para_bitmap;
1039 paragraph->u.gfx.pos = pos;
1040 switch (type)
1042 case 0x22:
1043 fetch_ushort(&format); /* hot spot */
1044 /* fall thru */
1045 case 0x03:
1046 switch (GET_SHORT(format, 0))
1048 case 0:
1049 HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2),
1050 paragraph);
1051 break;
1052 case 1:
1053 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
1054 GET_SHORT(format, 0),
1055 size, GET_SHORT(format, 2));
1056 HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4,
1057 paragraph);
1058 break;
1059 default:
1060 WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1061 break;
1063 break;
1064 case 0x05:
1065 WINE_FIXME("Got an embedded element %s\n", format + 6);
1066 break;
1067 default:
1068 WINE_FIXME("Got a type %d picture\n", type);
1069 break;
1071 if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1073 format += size;
1075 break;
1077 case 0x89:
1078 HLPFILE_FreeLink(attributes.link);
1079 attributes.link = NULL;
1080 format += 1;
1081 break;
1083 case 0x8B:
1084 case 0x8C:
1085 WINE_FIXME("NIY non-break space/hyphen\n");
1086 format += 1;
1087 break;
1089 #if 0
1090 case 0xA9:
1091 format += 2;
1092 break;
1093 #endif
1095 case 0xC8:
1096 case 0xCC:
1097 WINE_TRACE("macro => %s\n", format + 3);
1098 HLPFILE_FreeLink(attributes.link);
1099 attributes.link = HLPFILE_AllocLink(hlp_link_macro, (const char*)format + 3,
1100 0, !(*format & 4), -1);
1101 format += 3 + GET_USHORT(format, 1);
1102 break;
1104 case 0xE0:
1105 case 0xE1:
1106 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1107 format += 5;
1108 break;
1110 case 0xE2:
1111 case 0xE3:
1112 case 0xE6:
1113 case 0xE7:
1114 HLPFILE_FreeLink(attributes.link);
1115 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1116 hlpfile->lpszPath,
1117 GET_UINT(format, 1),
1118 !(*format & 4), -1);
1119 format += 5;
1120 break;
1122 case 0xEA:
1123 case 0xEB:
1124 case 0xEE:
1125 case 0xEF:
1127 char* ptr = (char*) format + 8;
1128 BYTE type = format[3];
1129 int wnd = -1;
1130 char* str;
1132 if (type == 1) wnd = *ptr++;
1133 if (type == 4 || type == 6)
1135 str = ptr;
1136 ptr += strlen(ptr) + 1;
1138 else
1139 str = hlpfile->lpszPath;
1140 if (type == 6)
1142 for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1144 if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1146 if (wnd == -1)
1147 WINE_WARN("Couldn't find window info for %s\n", ptr);
1149 HLPFILE_FreeLink(attributes.link);
1150 attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1151 str, GET_UINT(format, 4),
1152 !(*format & 1), wnd);
1154 format += 3 + GET_USHORT(format, 1);
1155 break;
1157 default:
1158 WINE_WARN("format %02x\n", *format);
1159 format++;
1163 if (text_end != (char*)buf + GET_UINT(buf, 0x10) + size)
1164 HeapFree(GetProcessHeap(), 0, text_end - size);
1165 return TRUE;
1168 /******************************************************************
1169 * HLPFILE_ReadFont
1173 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1175 BYTE *ref, *end;
1176 unsigned i, len, idx;
1177 unsigned face_num, dscr_num, face_offset, dscr_offset;
1178 BYTE flag, family;
1180 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1182 WINE_WARN("no subfile FONT\n");
1183 hlpfile->numFonts = 0;
1184 hlpfile->fonts = NULL;
1185 return FALSE;
1188 ref += 9;
1190 face_num = GET_USHORT(ref, 0);
1191 dscr_num = GET_USHORT(ref, 2);
1192 face_offset = GET_USHORT(ref, 4);
1193 dscr_offset = GET_USHORT(ref, 6);
1195 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1196 face_num, face_offset, dscr_num, dscr_offset);
1198 hlpfile->numFonts = dscr_num;
1199 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1201 len = (dscr_offset - face_offset) / face_num;
1202 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1203 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1204 for (i = 0; i < dscr_num; i++)
1206 flag = ref[dscr_offset + i * 11 + 0];
1207 family = ref[dscr_offset + i * 11 + 2];
1209 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2;
1210 hlpfile->fonts[i].LogFont.lfWidth = 0;
1211 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1212 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1213 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1214 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1215 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1216 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1217 hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
1218 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1219 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1220 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1221 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1223 switch (family)
1225 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1226 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1227 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1228 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1229 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1230 default: WINE_FIXME("Unknown family %u\n", family);
1232 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1234 if (idx < face_num)
1236 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1237 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
1239 else
1241 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1242 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1244 hlpfile->fonts[i].hFont = 0;
1245 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1246 ref[dscr_offset + i * 11 + 6],
1247 ref[dscr_offset + i * 11 + 7]);
1248 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1249 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1250 i, flag,
1251 X(0, "bold"),
1252 X(1, "italic"),
1253 X(2, "underline"),
1254 X(3, "strikeOut"),
1255 X(4, "dblUnderline"),
1256 X(5, "smallCaps"),
1257 ref[dscr_offset + i * 11 + 1],
1258 family,
1259 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1260 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1262 return TRUE;
1265 /***********************************************************************
1267 * HLPFILE_ReadFileToBuffer
1269 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1271 BYTE header[16], dummy[1];
1272 UINT size;
1274 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1276 /* sanity checks */
1277 if (GET_UINT(header, 0) != 0x00035F3F)
1278 {WINE_WARN("wrong header\n"); return FALSE;};
1280 size = GET_UINT(header, 12);
1281 file_buffer = HeapAlloc(GetProcessHeap(), 0, size + 1);
1282 if (!file_buffer) return FALSE;
1284 memcpy(file_buffer, header, 16);
1285 if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
1286 {WINE_WARN("filesize1\n"); return FALSE;};
1288 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1290 file_buffer[size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1292 return TRUE;
1295 /***********************************************************************
1297 * HLPFILE_FindSubFile
1299 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1301 BYTE *root = file_buffer + GET_UINT(file_buffer, 4);
1302 BYTE *end = file_buffer + GET_UINT(file_buffer, 12);
1303 BYTE *ptr;
1304 BYTE *bth;
1306 unsigned pgsize;
1307 unsigned pglast;
1308 unsigned nentries;
1309 unsigned i, n;
1311 bth = root + 9;
1313 /* FIXME: this should be using the EnumBTree functions from this file */
1314 pgsize = GET_USHORT(bth, 4);
1315 WINE_TRACE("%s => pgsize=%u #pg=%u rootpg=%u #lvl=%u\n",
1316 name, pgsize, GET_USHORT(bth, 30), GET_USHORT(bth, 26), GET_USHORT(bth, 32));
1318 ptr = bth + 38 + GET_USHORT(bth, 26) * pgsize;
1320 for (n = 1; n < GET_USHORT(bth, 32); n++)
1322 nentries = GET_USHORT(ptr, 2);
1323 pglast = GET_USHORT(ptr, 4);
1324 WINE_TRACE("[%u]: #entries=%u next=%u\n", n, nentries, pglast);
1326 ptr += 6;
1327 for (i = 0; i < nentries; i++)
1329 char *str = (char*) ptr;
1330 WINE_TRACE("<= %s\n", str);
1331 if (strcmp(name, str) < 0) break;
1332 ptr += strlen(str) + 1;
1333 pglast = GET_USHORT(ptr, 0);
1334 ptr += 2;
1336 ptr = bth + 38 + pglast * pgsize;
1339 nentries = GET_USHORT(ptr, 2);
1340 ptr += 8;
1341 for (i = 0; i < nentries; i++)
1343 char* fname = (char*)ptr;
1344 ptr += strlen(fname) + 1;
1345 WINE_TRACE("\\- %s\n", fname);
1346 if (strcmp(fname, name) == 0)
1348 *subbuf = file_buffer + GET_UINT(ptr, 0);
1349 *subend = *subbuf + GET_UINT(*subbuf, 0);
1350 if (file_buffer > *subbuf || *subbuf > *subend || *subend > end)
1352 WINE_WARN("size mismatch\n");
1353 return FALSE;
1355 return TRUE;
1357 ptr += 4;
1360 return FALSE;
1363 /***********************************************************************
1365 * HLPFILE_SystemCommands
1367 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1369 BYTE *buf, *ptr, *end;
1370 HLPFILE_MACRO *macro, **m;
1371 LPSTR p;
1372 unsigned short magic, minor, major, flags;
1374 hlpfile->lpszTitle = NULL;
1376 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1378 magic = GET_USHORT(buf + 9, 0);
1379 minor = GET_USHORT(buf + 9, 2);
1380 major = GET_USHORT(buf + 9, 4);
1381 /* gen date on 4 bytes */
1382 flags = GET_USHORT(buf + 9, 10);
1383 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1384 magic, major, minor, flags);
1385 if (magic != 0x036C || major != 1)
1386 {WINE_WARN("Wrong system header\n"); return FALSE;}
1387 if (minor <= 16) {WINE_WARN("too old file format (NIY)\n"); return FALSE;}
1388 if (flags & 8) {WINE_WARN("Unsupported yet page size\n"); return FALSE;}
1390 hlpfile->version = minor;
1391 hlpfile->flags = flags;
1393 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1395 char *str = (char*) ptr + 4;
1396 switch (GET_USHORT(ptr, 0))
1398 case 1:
1399 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1400 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1401 if (!hlpfile->lpszTitle) return FALSE;
1402 lstrcpy(hlpfile->lpszTitle, str);
1403 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1404 break;
1406 case 2:
1407 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1408 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1409 if (!hlpfile->lpszCopyright) return FALSE;
1410 lstrcpy(hlpfile->lpszCopyright, str);
1411 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1412 break;
1414 case 3:
1415 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1416 hlpfile->contents_start = GET_UINT(ptr, 4);
1417 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1418 break;
1420 case 4:
1421 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
1422 if (!macro) break;
1423 p = (char*)macro + sizeof(HLPFILE_MACRO);
1424 lstrcpy(p, str);
1425 macro->lpszMacro = p;
1426 macro->next = 0;
1427 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1428 *m = macro;
1429 break;
1431 case 6:
1432 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1434 if (hlpfile->windows)
1435 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1436 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1437 else
1438 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0,
1439 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1441 if (hlpfile->windows)
1443 unsigned flags = GET_USHORT(ptr, 4);
1444 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1446 if (flags & 0x0001) strcpy(wi->type, &str[2]);
1447 else wi->type[0] = '\0';
1448 if (flags & 0x0002) strcpy(wi->name, &str[12]);
1449 else wi->name[0] = '\0';
1450 if (flags & 0x0004) strcpy(wi->caption, &str[23]);
1451 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1452 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1453 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1454 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1455 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1456 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1457 wi->win_style = WS_OVERLAPPEDWINDOW;
1458 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1459 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1460 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",
1461 flags & 0x0001 ? 'T' : 't',
1462 flags & 0x0002 ? 'N' : 'n',
1463 flags & 0x0004 ? 'C' : 'c',
1464 flags & 0x0008 ? 'X' : 'x',
1465 flags & 0x0010 ? 'Y' : 'y',
1466 flags & 0x0020 ? 'W' : 'w',
1467 flags & 0x0040 ? 'H' : 'h',
1468 flags & 0x0080 ? 'S' : 's',
1469 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1470 wi->size.cx, wi->size.cy);
1472 break;
1473 default:
1474 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1477 if (!hlpfile->lpszTitle)
1478 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
1479 return TRUE;
1482 /***********************************************************************
1484 * HLPFILE_UncompressedLZ77_Size
1486 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1488 int i, newsize = 0;
1490 while (ptr < end)
1492 int mask = *ptr++;
1493 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1495 if (mask & 1)
1497 int code = GET_USHORT(ptr, 0);
1498 int len = 3 + (code >> 12);
1499 newsize += len;
1500 ptr += 2;
1502 else newsize++, ptr++;
1506 return newsize;
1509 /***********************************************************************
1511 * HLPFILE_UncompressLZ77
1513 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1515 int i;
1517 while (ptr < end)
1519 int mask = *ptr++;
1520 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1522 if (mask & 1)
1524 int code = GET_USHORT(ptr, 0);
1525 int len = 3 + (code >> 12);
1526 int offset = code & 0xfff;
1528 * We must copy byte-by-byte here. We cannot use memcpy nor
1529 * memmove here. Just example:
1530 * a[]={1,2,3,4,5,6,7,8,9,10}
1531 * newptr=a+2;
1532 * offset=1;
1533 * We expect:
1534 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
1536 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
1537 ptr += 2;
1539 else *newptr++ = *ptr++;
1543 return newptr;
1546 /***********************************************************************
1548 * HLPFILE_UncompressLZ77_Phrases
1550 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1552 UINT i, num, dec_size;
1553 BYTE *buf, *end;
1555 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1557 num = phrases.num = GET_USHORT(buf, 9);
1558 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1560 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1562 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1563 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1564 if (!phrases.offsets || !phrases.buffer) return FALSE;
1566 for (i = 0; i <= num; i++)
1567 phrases.offsets[i] = GET_USHORT(buf, 0x11 + 2 * i) - 2 * num - 2;
1569 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)phrases.buffer);
1571 hlpfile->hasPhrases = TRUE;
1572 return TRUE;
1575 /***********************************************************************
1577 * HLPFILE_Uncompress_Phrases40
1579 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1581 UINT num;
1582 INT dec_size, cpr_size;
1583 BYTE *buf_idx, *end_idx;
1584 BYTE *buf_phs, *end_phs;
1585 long* ptr, mask = 0;
1586 unsigned int i;
1587 unsigned short bc, n;
1589 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1590 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1592 ptr = (long*)(buf_idx + 9 + 28);
1593 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1594 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1596 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1597 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1598 GET_UINT(buf_idx, 9 + 0),
1599 GET_UINT(buf_idx, 9 + 4),
1600 GET_UINT(buf_idx, 9 + 8),
1601 GET_UINT(buf_idx, 9 + 12),
1602 GET_UINT(buf_idx, 9 + 16),
1603 GET_UINT(buf_idx, 9 + 20),
1604 GET_USHORT(buf_idx, 9 + 24),
1605 GET_USHORT(buf_idx, 9 + 26));
1607 dec_size = GET_UINT(buf_idx, 9 + 12);
1608 cpr_size = GET_UINT(buf_idx, 9 + 16);
1610 if (dec_size != cpr_size &&
1611 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1613 WINE_WARN("size mismatch %u %u\n",
1614 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1615 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1618 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1619 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1620 if (!phrases.offsets || !phrases.buffer) return FALSE;
1622 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1624 phrases.offsets[0] = 0;
1625 for (i = 0; i < num; i++)
1627 for (n = 1; getbit(); n += 1 << bc);
1628 if (getbit()) n++;
1629 if (bc > 1 && getbit()) n += 2;
1630 if (bc > 2 && getbit()) n += 4;
1631 if (bc > 3 && getbit()) n += 8;
1632 if (bc > 4 && getbit()) n += 16;
1633 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1635 #undef getbit
1637 if (dec_size == cpr_size)
1638 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1639 else
1640 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)phrases.buffer);
1642 hlpfile->hasPhrases = FALSE;
1643 return TRUE;
1646 /***********************************************************************
1648 * HLPFILE_Uncompress_Topic
1650 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1652 BYTE *buf, *ptr, *end, *newptr;
1653 unsigned int i, newsize = 0;
1655 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1656 {WINE_WARN("topic0\n"); return FALSE;}
1658 switch (hlpfile->flags & (8|4))
1660 case 8:
1661 WINE_FIXME("Unsupported format\n");
1662 return FALSE;
1663 case 4:
1664 buf += 9;
1665 topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
1667 for (i = 0; i < topic.wMapLen; i++)
1669 ptr = buf + i * 0x1000;
1671 /* I don't know why, it's necessary for printman.hlp */
1672 if (ptr + 0x44 > end) ptr = end - 0x44;
1674 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + 0x1000));
1677 topic.map = HeapAlloc(GetProcessHeap(), 0,
1678 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1679 if (!topic.map) return FALSE;
1680 newptr = (BYTE*)(topic.map + topic.wMapLen);
1681 topic.end = newptr + newsize;
1683 for (i = 0; i < topic.wMapLen; i++)
1685 ptr = buf + i * 0x1000;
1686 if (ptr + 0x44 > end) ptr = end - 0x44;
1688 topic.map[i] = newptr;
1689 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + 0x1000), newptr);
1691 break;
1692 case 0:
1693 /* basically, we need to copy the 0x1000 byte pages (removing the first 0x0C) in
1694 * one single are in memory
1696 #define DST_LEN (0x1000 - 0x0C)
1697 buf += 9;
1698 newsize = end - buf;
1699 /* number of destination pages */
1700 topic.wMapLen = (newsize - 1) / DST_LEN + 1;
1701 topic.map = HeapAlloc(GetProcessHeap(), 0,
1702 topic.wMapLen * (sizeof(topic.map[0]) + DST_LEN));
1703 if (!topic.map) return FALSE;
1704 newptr = (BYTE*)(topic.map + topic.wMapLen);
1705 topic.end = newptr + newsize;
1707 for (i = 0; i < topic.wMapLen; i++)
1709 topic.map[i] = newptr + i * DST_LEN;
1710 memcpy(topic.map[i], buf + i * 0x1000 + 0x0C, DST_LEN);
1712 #undef DST_LEN
1713 break;
1715 return TRUE;
1718 /***********************************************************************
1720 * HLPFILE_Uncompress2
1723 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1725 BYTE *phptr, *phend;
1726 UINT code;
1727 UINT index;
1729 while (ptr < end && newptr < newend)
1731 if (!*ptr || *ptr >= 0x10)
1732 *newptr++ = *ptr++;
1733 else
1735 code = 0x100 * ptr[0] + ptr[1];
1736 index = (code - 0x100) / 2;
1738 phptr = (BYTE*)phrases.buffer + phrases.offsets[index];
1739 phend = (BYTE*)phrases.buffer + phrases.offsets[index + 1];
1741 if (newptr + (phend - phptr) > newend)
1743 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1744 newptr, newend, phend - phptr);
1745 return;
1747 memcpy(newptr, phptr, phend - phptr);
1748 newptr += phend - phptr;
1749 if (code & 1) *newptr++ = ' ';
1751 ptr += 2;
1754 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1757 /******************************************************************
1758 * HLPFILE_Uncompress3
1762 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1763 const BYTE* src, const BYTE* src_end)
1765 unsigned int idx, len;
1767 for (; src < src_end; src++)
1769 if ((*src & 1) == 0)
1771 idx = *src / 2;
1772 if (idx > phrases.num)
1774 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1775 len = 0;
1777 else
1779 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1780 if (dst + len <= dst_end)
1781 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1784 else if ((*src & 0x03) == 0x01)
1786 idx = (*src + 1) * 64;
1787 idx += *++src;
1788 if (idx > phrases.num)
1790 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1791 len = 0;
1793 else
1795 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1796 if (dst + len <= dst_end)
1797 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1800 else if ((*src & 0x07) == 0x03)
1802 len = (*src / 8) + 1;
1803 if (dst + len <= dst_end)
1804 memcpy(dst, src + 1, len);
1805 src += len;
1807 else
1809 len = (*src / 16) + 1;
1810 if (dst + len <= dst_end)
1811 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1813 dst += len;
1816 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1817 return TRUE;
1820 /******************************************************************
1821 * HLPFILE_UncompressRLE
1825 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1827 BYTE ch;
1828 BYTE* sdst = *dst + dstsz;
1830 while (src < end)
1832 ch = *src++;
1833 if (ch & 0x80)
1835 ch &= 0x7F;
1836 if ((*dst) + ch <= sdst)
1837 memcpy(*dst, src, ch);
1838 src += ch;
1840 else
1842 if ((*dst) + ch <= sdst)
1843 memset(*dst, (char)*src, ch);
1844 src++;
1846 *dst += ch;
1848 if (*dst != sdst)
1849 WINE_WARN("Buffer X-flow: d(%u) instead of d(%u)\n",
1850 *dst - (sdst - dstsz), dstsz);
1853 /******************************************************************
1854 * HLPFILE_EnumBTreeLeaves
1858 static void HLPFILE_EnumBTreeLeaves(const BYTE* buf, const BYTE* end, unsigned (*fn)(const BYTE*, void*), void* user)
1860 unsigned psize, pnext;
1861 unsigned num, nlvl;
1862 const BYTE* ptr;
1864 num = GET_UINT(buf, 9 + 34);
1865 psize = GET_USHORT(buf, 9 + 4);
1866 nlvl = GET_USHORT(buf, 9 + 32);
1867 pnext = GET_USHORT(buf, 9 + 26);
1869 WINE_TRACE("BTree: #entries=%u pagSize=%u #levels=%u #pages=%u root=%u struct%16s\n",
1870 num, psize, nlvl, GET_USHORT(buf, 9 + 30), pnext, buf + 9 + 6);
1871 if (!num) return;
1873 while (--nlvl > 0)
1875 ptr = (buf + 9 + 38) + pnext * psize;
1876 WINE_TRACE("BTree: (index[%u]) unused=%u #entries=%u <%u\n",
1877 pnext, GET_USHORT(ptr, 0), GET_USHORT(ptr, 2), GET_USHORT(ptr, 4));
1878 pnext = GET_USHORT(ptr, 4);
1880 while (pnext != 0xFFFF)
1882 const BYTE* node_page;
1883 unsigned short limit;
1885 node_page = ptr = (buf + 9 + 38) + pnext * psize;
1886 limit = GET_USHORT(ptr, 2);
1887 WINE_TRACE("BTree: (leaf [%u]) unused=%u #entries=%u <%u >%u\n",
1888 pnext, GET_USHORT(ptr, 0), limit, GET_USHORT(ptr, 4), GET_USHORT(ptr, 6));
1889 ptr += 8;
1890 while (limit--)
1891 ptr += (fn)(ptr, user);
1892 pnext = GET_USHORT(node_page, 6);
1896 struct myfncb {
1897 HLPFILE* hlpfile;
1898 int i;
1901 static unsigned myfn(const BYTE* ptr, void* user)
1903 struct myfncb* m = user;
1905 m->hlpfile->Context[m->i].lHash = GET_UINT(ptr, 0);
1906 m->hlpfile->Context[m->i].offset = GET_UINT(ptr, 4);
1907 m->i++;
1908 return 8;
1911 /***********************************************************************
1913 * HLPFILE_GetContext
1915 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
1917 BYTE *cbuf, *cend;
1918 struct myfncb m;
1919 unsigned clen;
1921 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
1923 clen = GET_UINT(cbuf, 0x2b);
1924 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen * sizeof(HLPFILE_CONTEXT));
1925 if (!hlpfile->Context) return FALSE;
1926 hlpfile->wContextLen = clen;
1928 m.hlpfile = hlpfile;
1929 m.i = 0;
1930 HLPFILE_EnumBTreeLeaves(cbuf, cend, myfn, &m);
1932 return TRUE;
1935 /***********************************************************************
1937 * HLPFILE_GetMap
1939 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
1941 BYTE *cbuf, *cend;
1942 unsigned entries, i;
1944 if (!HLPFILE_FindSubFile("|CTXOMAP", &cbuf, &cend)) {WINE_WARN("no map section\n"); return FALSE;}
1946 entries = GET_USHORT(cbuf, 9);
1947 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
1948 if (!hlpfile->Map) return FALSE;
1949 hlpfile->wMapLen = entries;
1950 for (i = 0; i < entries; i++)
1952 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
1953 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
1955 return TRUE;
1958 /******************************************************************
1959 * HLPFILE_DeleteLink
1963 void HLPFILE_FreeLink(HLPFILE_LINK* link)
1965 if (link && !--link->wRefCount)
1966 HeapFree(GetProcessHeap(), 0, link);
1969 /***********************************************************************
1971 * HLPFILE_DeleteParagraph
1973 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
1975 HLPFILE_PARAGRAPH* next;
1977 while (paragraph)
1979 next = paragraph->next;
1981 if (paragraph->cookie == para_metafile)
1982 DeleteMetaFile(paragraph->u.gfx.u.mfp.hMF);
1984 HLPFILE_FreeLink(paragraph->link);
1986 HeapFree(GetProcessHeap(), 0, paragraph);
1987 paragraph = next;
1991 /***********************************************************************
1993 * DeleteMacro
1995 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
1997 HLPFILE_MACRO* next;
1999 while (macro)
2001 next = macro->next;
2002 HeapFree(GetProcessHeap(), 0, macro);
2003 macro = next;
2007 /***********************************************************************
2009 * DeletePage
2011 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
2013 HLPFILE_PAGE* next;
2015 while (page)
2017 next = page->next;
2018 HLPFILE_DeleteParagraph(page->first_paragraph);
2019 HLPFILE_DeleteMacro(page->first_macro);
2020 HeapFree(GetProcessHeap(), 0, page);
2021 page = next;
2025 /***********************************************************************
2027 * HLPFILE_FreeHlpFile
2029 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
2031 unsigned i;
2033 if (!hlpfile || --hlpfile->wRefCount > 0) return;
2035 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
2036 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
2037 else first_hlpfile = hlpfile->next;
2039 if (hlpfile->numFonts)
2041 for (i = 0; i < hlpfile->numFonts; i++)
2043 DeleteObject(hlpfile->fonts[i].hFont);
2045 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
2048 if (hlpfile->numBmps)
2050 for (i = 0; i < hlpfile->numBmps; i++)
2052 DeleteObject(hlpfile->bmps[i]);
2054 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
2057 HLPFILE_DeletePage(hlpfile->first_page);
2058 HLPFILE_DeleteMacro(hlpfile->first_macro);
2060 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
2061 HeapFree(GetProcessHeap(), 0, hlpfile->Context);
2062 HeapFree(GetProcessHeap(), 0, hlpfile->Map);
2063 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
2064 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
2065 HeapFree(GetProcessHeap(), 0, hlpfile);