push 3de9b6d6ec9c4ab8dfeeb76c2f7fb86ab1d5c9a7
[wine/hacks.git] / programs / winhelp / hlpfile.c
blob3c34a06e0c94b0a52de424c52b080ec0c8162789
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)
585 HeapFree(GetProcessHeap(), 0, tmp);
586 return FALSE;
588 HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
589 if (tmp2 - dst != sz)
590 WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
591 HeapFree(GetProcessHeap(), 0, tmp);
592 break;
593 default:
594 WINE_FIXME("Unsupported packing %u\n", packing);
595 return NULL;
597 return dst;
600 /******************************************************************
601 * HLPFILE_LoadBitmap
605 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack,
606 HLPFILE_PARAGRAPH* paragraph)
608 BYTE* ptr;
609 BYTE* pict_beg;
610 BITMAPINFO* bi;
611 unsigned long off, csz;
612 HDC hdc;
614 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
615 if (!bi) return FALSE;
617 ptr = beg + 2; /* for type and pack */
619 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
620 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
621 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
622 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
623 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
624 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
625 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
626 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
627 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
628 bi->bmiHeader.biCompression = BI_RGB;
629 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
630 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
631 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
632 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
633 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
634 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
636 csz = fetch_ulong(&ptr);
637 fetch_ulong(&ptr); /* hotspot size */
639 off = GET_UINT(ptr, 0); ptr += 4;
640 /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
642 /* now read palette info */
643 if (type == 0x06)
645 unsigned nc = bi->bmiHeader.biClrUsed;
646 unsigned i;
648 /* not quite right, especially for bitfields type of compression */
649 if (!nc && bi->bmiHeader.biBitCount <= 8)
650 nc = 1 << bi->bmiHeader.biBitCount;
652 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
653 if (!bi) return FALSE;
654 for (i = 0; i < nc; i++)
656 bi->bmiColors[i].rgbBlue = ptr[0];
657 bi->bmiColors[i].rgbGreen = ptr[1];
658 bi->bmiColors[i].rgbRed = ptr[2];
659 bi->bmiColors[i].rgbReserved = 0;
660 ptr += 4;
663 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
665 paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
666 CBM_INIT, pict_beg,
667 bi, DIB_RGB_COLORS);
668 ReleaseDC(0, hdc);
669 if (!paragraph->u.gfx.u.bmp.hBitmap)
670 WINE_ERR("Couldn't create bitmap\n");
672 HeapFree(GetProcessHeap(), 0, bi);
673 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
675 return TRUE;
678 /******************************************************************
679 * HLPFILE_LoadMetaFile
683 static BOOL HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
685 BYTE* ptr;
686 unsigned long size, csize;
687 unsigned long off, hsoff;
688 BYTE* bits;
689 LPMETAFILEPICT lpmfp;
691 WINE_TRACE("Loading metafile\n");
693 ptr = beg + 2; /* for type and pack */
695 lpmfp = &paragraph->u.gfx.u.mfp;
696 lpmfp->mm = fetch_ushort(&ptr); /* mapping mode */
698 lpmfp->xExt = GET_USHORT(ptr, 0);
699 lpmfp->yExt = GET_USHORT(ptr, 2);
700 ptr += 4;
702 size = fetch_ulong(&ptr); /* decompressed size */
703 csize = fetch_ulong(&ptr); /* compressed size */
704 fetch_ulong(&ptr); /* hotspot size */
705 off = GET_UINT(ptr, 0);
706 hsoff = GET_UINT(ptr, 4);
707 ptr += 8;
709 WINE_TRACE("sz=%lu csz=%lu (%d,%d) offs=%lu/%u,%lu\n",
710 size, csize, lpmfp->xExt, lpmfp->yExt, off, ptr - beg, hsoff);
712 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
713 if (!bits) return FALSE;
715 paragraph->cookie = para_metafile;
717 lpmfp->hMF = SetMetaFileBitsEx(size, bits);
719 if (!lpmfp->hMF)
720 WINE_FIXME("Couldn't load metafile\n");
722 if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
724 return TRUE;
727 /******************************************************************
728 * HLPFILE_LoadGfxByAddr
732 static BOOL HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
733 unsigned long size,
734 HLPFILE_PARAGRAPH* paragraph)
736 unsigned i, numpict;
738 numpict = GET_USHORT(ref, 2);
739 WINE_TRACE("Got picture magic=%04x #=%d\n",
740 GET_USHORT(ref, 0), numpict);
742 for (i = 0; i < numpict; i++)
744 BYTE* beg;
745 BYTE* ptr;
746 BYTE type, pack;
748 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
749 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
751 type = *ptr++;
752 pack = *ptr++;
754 switch (type)
756 case 5: /* device dependent bmp */
757 case 6: /* device independent bmp */
758 HLPFILE_LoadBitmap(beg, type, pack, paragraph);
759 break;
760 case 8:
761 HLPFILE_LoadMetaFile(beg, pack, paragraph);
762 break;
763 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
766 /* FIXME: hotspots */
768 /* FIXME: implement support for multiple picture format */
769 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
770 break;
772 return TRUE;
775 /******************************************************************
776 * HLPFILE_LoadGfxByIndex
780 static BOOL HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index,
781 HLPFILE_PARAGRAPH* paragraph)
783 char tmp[16];
784 BYTE *ref, *end;
785 BOOL ret;
787 WINE_TRACE("Loading picture #%d\n", index);
789 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
791 paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
792 return TRUE;
795 sprintf(tmp, "|bm%u", index);
797 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
799 ref += 9;
801 ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
803 /* cache bitmap */
804 if (ret && paragraph->cookie == para_bitmap)
806 if (index >= hlpfile->numBmps)
808 hlpfile->numBmps = index + 1;
809 if (hlpfile->bmps)
810 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
811 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
812 else
813 hlpfile->bmps = HeapAlloc(GetProcessHeap(), 0,
814 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
817 hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
819 return ret;
822 /******************************************************************
823 * HLPFILE_AllocLink
827 static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
828 BOOL clrChange, unsigned wnd)
830 HLPFILE_LINK* link;
831 char* link_str;
833 /* FIXME: should build a string table for the attributes.link.lpszPath
834 * they are reallocated for each link
836 link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
837 if (!link) return NULL;
839 link->cookie = cookie;
840 link->lpszString = link_str = (char*)link + sizeof(HLPFILE_LINK);
841 strcpy(link_str, str);
842 link->lHash = hash;
843 link->bClrChange = clrChange ? 1 : 0;
844 link->window = wnd;
845 link->wRefCount = 1;
847 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
848 link->cookie, link->lpszString,
849 link->lHash, link->window);
850 return link;
853 /***********************************************************************
855 * HLPFILE_AddParagraph
857 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
859 HLPFILE_PAGE *page;
860 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
861 UINT textsize;
862 BYTE *format, *format_end;
863 char *text, *text_base, *text_end;
864 long size;
865 unsigned short bits;
866 unsigned nc, ncol = 1;
868 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
870 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
871 for (paragraphptr = &page->first_paragraph; *paragraphptr;
872 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
874 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
876 size = GET_UINT(buf, 0x4);
877 text = text_base = HeapAlloc(GetProcessHeap(), 0, size);
878 if (!text) return FALSE;
879 if (hlpfile->hasPhrases)
881 HLPFILE_Uncompress2(buf + GET_UINT(buf, 0x10), end, (BYTE*)text, (BYTE*)text + size);
883 else
885 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
887 /* block is compressed */
888 HLPFILE_Uncompress3(text, text + size, buf + GET_UINT(buf, 0x10), end);
890 else
892 text = (char*)buf + GET_UINT(buf, 0x10);
895 text_end = text + size;
897 format = buf + 0x15;
898 format_end = buf + GET_UINT(buf, 0x10);
900 fetch_long(&format);
901 *len = fetch_ushort(&format);
903 if (buf[0x14] == 0x23)
905 char type;
907 ncol = *format++;
909 WINE_TRACE("#cols %u\n", ncol);
910 type = *format++;
911 if (type == 0 || type == 2)
912 format += 2;
913 format += ncol * 4;
916 for (nc = 0; nc < ncol; nc++)
918 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
919 if (buf[0x14] == 0x23)
920 format += 5;
921 format += 4;
922 bits = GET_USHORT(format, 0); format += 2;
923 if (bits & 0x0001) fetch_long(&format);
924 if (bits & 0x0002) fetch_short(&format);
925 if (bits & 0x0004) fetch_short(&format);
926 if (bits & 0x0008) fetch_short(&format);
927 if (bits & 0x0010) fetch_short(&format);
928 if (bits & 0x0020) fetch_short(&format);
929 if (bits & 0x0040) fetch_short(&format);
930 if (bits & 0x0100) format += 3;
931 if (bits & 0x0200)
933 int ntab = fetch_short(&format);
934 unsigned short ts;
936 while (ntab-- > 0)
938 ts = fetch_ushort(&format);
939 if (ts & 0x4000) fetch_ushort(&format);
942 /* 0x0400, 0x0800 and 0x1000 don't need space */
943 if ((bits & 0xE080) != 0)
944 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
946 while (text < text_end && format < format_end)
948 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
949 textsize = strlen(text) + 1;
950 if (textsize > 1)
952 paragraph = HeapAlloc(GetProcessHeap(), 0,
953 sizeof(HLPFILE_PARAGRAPH) + textsize);
954 if (!paragraph) return FALSE;
955 *paragraphptr = paragraph;
956 paragraphptr = &paragraph->next;
958 paragraph->next = NULL;
959 paragraph->link = attributes.link;
960 if (paragraph->link) paragraph->link->wRefCount++;
961 paragraph->cookie = para_normal_text;
962 paragraph->u.text.wFont = attributes.wFont;
963 paragraph->u.text.wVSpace = attributes.wVSpace;
964 paragraph->u.text.wHSpace = attributes.wHSpace;
965 paragraph->u.text.wIndent = attributes.wIndent;
966 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
967 strcpy(paragraph->u.text.lpszText, text);
969 attributes.wVSpace = 0;
970 attributes.wHSpace = 0;
972 /* else: null text, keep on storing attributes */
973 text += textsize;
975 if (*format == 0xff)
977 format++;
978 break;
981 WINE_TRACE("format=%02x\n", *format);
982 switch (*format)
984 case 0x20:
985 WINE_FIXME("NIY20\n");
986 format += 5;
987 break;
989 case 0x21:
990 WINE_FIXME("NIY21\n");
991 format += 3;
992 break;
994 case 0x80:
995 attributes.wFont = GET_USHORT(format, 1);
996 WINE_TRACE("Changing font to %d\n", attributes.wFont);
997 format += 3;
998 break;
1000 case 0x81:
1001 attributes.wVSpace++;
1002 format += 1;
1003 break;
1005 case 0x82:
1006 attributes.wVSpace++;
1007 attributes.wIndent = 0;
1008 format += 1;
1009 break;
1011 case 0x83:
1012 attributes.wIndent++;
1013 format += 1;
1014 break;
1016 #if 0
1017 case 0x84:
1018 format += 3;
1019 break;
1020 #endif
1022 case 0x86:
1023 case 0x87:
1024 case 0x88:
1026 BYTE pos = (*format - 0x86);
1027 BYTE type = format[1];
1028 long size;
1030 format += 2;
1031 size = fetch_long(&format);
1033 paragraph = HeapAlloc(GetProcessHeap(), 0,
1034 sizeof(HLPFILE_PARAGRAPH) + textsize);
1035 if (!paragraph) return FALSE;
1036 *paragraphptr = paragraph;
1037 paragraphptr = &paragraph->next;
1039 paragraph->next = NULL;
1040 paragraph->link = attributes.link;
1041 if (paragraph->link) paragraph->link->wRefCount++;
1042 paragraph->cookie = para_bitmap;
1043 paragraph->u.gfx.pos = pos;
1044 switch (type)
1046 case 0x22:
1047 fetch_ushort(&format); /* hot spot */
1048 /* fall thru */
1049 case 0x03:
1050 switch (GET_SHORT(format, 0))
1052 case 0:
1053 HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2),
1054 paragraph);
1055 break;
1056 case 1:
1057 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
1058 GET_SHORT(format, 0),
1059 size, GET_SHORT(format, 2));
1060 HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4,
1061 paragraph);
1062 break;
1063 default:
1064 WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1065 break;
1067 break;
1068 case 0x05:
1069 WINE_FIXME("Got an embedded element %s\n", format + 6);
1070 break;
1071 default:
1072 WINE_FIXME("Got a type %d picture\n", type);
1073 break;
1075 if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1077 format += size;
1079 break;
1081 case 0x89:
1082 HLPFILE_FreeLink(attributes.link);
1083 attributes.link = NULL;
1084 format += 1;
1085 break;
1087 case 0x8B:
1088 case 0x8C:
1089 WINE_FIXME("NIY non-break space/hyphen\n");
1090 format += 1;
1091 break;
1093 #if 0
1094 case 0xA9:
1095 format += 2;
1096 break;
1097 #endif
1099 case 0xC8:
1100 case 0xCC:
1101 WINE_TRACE("macro => %s\n", format + 3);
1102 HLPFILE_FreeLink(attributes.link);
1103 attributes.link = HLPFILE_AllocLink(hlp_link_macro, (const char*)format + 3,
1104 0, !(*format & 4), -1);
1105 format += 3 + GET_USHORT(format, 1);
1106 break;
1108 case 0xE0:
1109 case 0xE1:
1110 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1111 format += 5;
1112 break;
1114 case 0xE2:
1115 case 0xE3:
1116 case 0xE6:
1117 case 0xE7:
1118 HLPFILE_FreeLink(attributes.link);
1119 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1120 hlpfile->lpszPath,
1121 GET_UINT(format, 1),
1122 !(*format & 4), -1);
1123 format += 5;
1124 break;
1126 case 0xEA:
1127 case 0xEB:
1128 case 0xEE:
1129 case 0xEF:
1131 char* ptr = (char*) format + 8;
1132 BYTE type = format[3];
1133 int wnd = -1;
1134 char* str;
1136 if (type == 1) wnd = *ptr++;
1137 if (type == 4 || type == 6)
1139 str = ptr;
1140 ptr += strlen(ptr) + 1;
1142 else
1143 str = hlpfile->lpszPath;
1144 if (type == 6)
1146 for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1148 if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1150 if (wnd == -1)
1151 WINE_WARN("Couldn't find window info for %s\n", ptr);
1153 HLPFILE_FreeLink(attributes.link);
1154 attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1155 str, GET_UINT(format, 4),
1156 !(*format & 1), wnd);
1158 format += 3 + GET_USHORT(format, 1);
1159 break;
1161 default:
1162 WINE_WARN("format %02x\n", *format);
1163 format++;
1167 HeapFree(GetProcessHeap(), 0, text_base);
1168 return TRUE;
1171 /******************************************************************
1172 * HLPFILE_ReadFont
1176 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1178 BYTE *ref, *end;
1179 unsigned i, len, idx;
1180 unsigned face_num, dscr_num, face_offset, dscr_offset;
1181 BYTE flag, family;
1183 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1185 WINE_WARN("no subfile FONT\n");
1186 hlpfile->numFonts = 0;
1187 hlpfile->fonts = NULL;
1188 return FALSE;
1191 ref += 9;
1193 face_num = GET_USHORT(ref, 0);
1194 dscr_num = GET_USHORT(ref, 2);
1195 face_offset = GET_USHORT(ref, 4);
1196 dscr_offset = GET_USHORT(ref, 6);
1198 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1199 face_num, face_offset, dscr_num, dscr_offset);
1201 hlpfile->numFonts = dscr_num;
1202 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1204 len = (dscr_offset - face_offset) / face_num;
1205 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1206 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1207 for (i = 0; i < dscr_num; i++)
1209 flag = ref[dscr_offset + i * 11 + 0];
1210 family = ref[dscr_offset + i * 11 + 2];
1212 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2;
1213 hlpfile->fonts[i].LogFont.lfWidth = 0;
1214 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1215 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1216 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1217 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1218 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1219 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1220 hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
1221 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1222 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1223 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1224 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1226 switch (family)
1228 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1229 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1230 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1231 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1232 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1233 default: WINE_FIXME("Unknown family %u\n", family);
1235 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1237 if (idx < face_num)
1239 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1240 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
1242 else
1244 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1245 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1247 hlpfile->fonts[i].hFont = 0;
1248 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1249 ref[dscr_offset + i * 11 + 6],
1250 ref[dscr_offset + i * 11 + 7]);
1251 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1252 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1253 i, flag,
1254 X(0, "bold"),
1255 X(1, "italic"),
1256 X(2, "underline"),
1257 X(3, "strikeOut"),
1258 X(4, "dblUnderline"),
1259 X(5, "smallCaps"),
1260 ref[dscr_offset + i * 11 + 1],
1261 family,
1262 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1263 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1265 return TRUE;
1268 /***********************************************************************
1270 * HLPFILE_ReadFileToBuffer
1272 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1274 BYTE header[16], dummy[1];
1275 UINT size;
1277 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1279 /* sanity checks */
1280 if (GET_UINT(header, 0) != 0x00035F3F)
1281 {WINE_WARN("wrong header\n"); return FALSE;};
1283 size = GET_UINT(header, 12);
1284 file_buffer = HeapAlloc(GetProcessHeap(), 0, size + 1);
1285 if (!file_buffer) return FALSE;
1287 memcpy(file_buffer, header, 16);
1288 if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
1289 {WINE_WARN("filesize1\n"); return FALSE;};
1291 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1293 file_buffer[size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1295 return TRUE;
1298 /***********************************************************************
1300 * HLPFILE_FindSubFile
1302 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1304 BYTE *root = file_buffer + GET_UINT(file_buffer, 4);
1305 BYTE *end = file_buffer + GET_UINT(file_buffer, 12);
1306 BYTE *ptr;
1307 BYTE *bth;
1309 unsigned pgsize;
1310 unsigned pglast;
1311 unsigned nentries;
1312 unsigned i, n;
1314 bth = root + 9;
1316 /* FIXME: this should be using the EnumBTree functions from this file */
1317 pgsize = GET_USHORT(bth, 4);
1318 WINE_TRACE("%s => pgsize=%u #pg=%u rootpg=%u #lvl=%u\n",
1319 name, pgsize, GET_USHORT(bth, 30), GET_USHORT(bth, 26), GET_USHORT(bth, 32));
1321 ptr = bth + 38 + GET_USHORT(bth, 26) * pgsize;
1323 for (n = 1; n < GET_USHORT(bth, 32); n++)
1325 nentries = GET_USHORT(ptr, 2);
1326 pglast = GET_USHORT(ptr, 4);
1327 WINE_TRACE("[%u]: #entries=%u next=%u\n", n, nentries, pglast);
1329 ptr += 6;
1330 for (i = 0; i < nentries; i++)
1332 char *str = (char*) ptr;
1333 WINE_TRACE("<= %s\n", str);
1334 if (strcmp(name, str) < 0) break;
1335 ptr += strlen(str) + 1;
1336 pglast = GET_USHORT(ptr, 0);
1337 ptr += 2;
1339 ptr = bth + 38 + pglast * pgsize;
1342 nentries = GET_USHORT(ptr, 2);
1343 ptr += 8;
1344 for (i = 0; i < nentries; i++)
1346 char* fname = (char*)ptr;
1347 ptr += strlen(fname) + 1;
1348 WINE_TRACE("\\- %s\n", fname);
1349 if (strcmp(fname, name) == 0)
1351 *subbuf = file_buffer + GET_UINT(ptr, 0);
1352 *subend = *subbuf + GET_UINT(*subbuf, 0);
1353 if (file_buffer > *subbuf || *subbuf > *subend || *subend > end)
1355 WINE_WARN("size mismatch\n");
1356 return FALSE;
1358 return TRUE;
1360 ptr += 4;
1363 return FALSE;
1366 /***********************************************************************
1368 * HLPFILE_SystemCommands
1370 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1372 BYTE *buf, *ptr, *end;
1373 HLPFILE_MACRO *macro, **m;
1374 LPSTR p;
1375 unsigned short magic, minor, major, flags;
1377 hlpfile->lpszTitle = NULL;
1379 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1381 magic = GET_USHORT(buf + 9, 0);
1382 minor = GET_USHORT(buf + 9, 2);
1383 major = GET_USHORT(buf + 9, 4);
1384 /* gen date on 4 bytes */
1385 flags = GET_USHORT(buf + 9, 10);
1386 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1387 magic, major, minor, flags);
1388 if (magic != 0x036C || major != 1)
1389 {WINE_WARN("Wrong system header\n"); return FALSE;}
1390 if (minor <= 16) {WINE_WARN("too old file format (NIY)\n"); return FALSE;}
1391 if (flags & 8) {WINE_WARN("Unsupported yet page size\n"); return FALSE;}
1393 hlpfile->version = minor;
1394 hlpfile->flags = flags;
1396 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1398 char *str = (char*) ptr + 4;
1399 switch (GET_USHORT(ptr, 0))
1401 case 1:
1402 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1403 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1404 if (!hlpfile->lpszTitle) return FALSE;
1405 lstrcpy(hlpfile->lpszTitle, str);
1406 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1407 break;
1409 case 2:
1410 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1411 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1412 if (!hlpfile->lpszCopyright) return FALSE;
1413 lstrcpy(hlpfile->lpszCopyright, str);
1414 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1415 break;
1417 case 3:
1418 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1419 hlpfile->contents_start = GET_UINT(ptr, 4);
1420 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1421 break;
1423 case 4:
1424 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
1425 if (!macro) break;
1426 p = (char*)macro + sizeof(HLPFILE_MACRO);
1427 lstrcpy(p, str);
1428 macro->lpszMacro = p;
1429 macro->next = 0;
1430 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1431 *m = macro;
1432 break;
1434 case 6:
1435 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1437 if (hlpfile->windows)
1438 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1439 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1440 else
1441 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0,
1442 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1444 if (hlpfile->windows)
1446 unsigned flags = GET_USHORT(ptr, 4);
1447 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1449 if (flags & 0x0001) strcpy(wi->type, &str[2]);
1450 else wi->type[0] = '\0';
1451 if (flags & 0x0002) strcpy(wi->name, &str[12]);
1452 else wi->name[0] = '\0';
1453 if (flags & 0x0004) strcpy(wi->caption, &str[23]);
1454 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1455 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1456 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1457 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1458 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1459 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1460 wi->win_style = WS_OVERLAPPEDWINDOW;
1461 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1462 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1463 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",
1464 flags & 0x0001 ? 'T' : 't',
1465 flags & 0x0002 ? 'N' : 'n',
1466 flags & 0x0004 ? 'C' : 'c',
1467 flags & 0x0008 ? 'X' : 'x',
1468 flags & 0x0010 ? 'Y' : 'y',
1469 flags & 0x0020 ? 'W' : 'w',
1470 flags & 0x0040 ? 'H' : 'h',
1471 flags & 0x0080 ? 'S' : 's',
1472 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1473 wi->size.cx, wi->size.cy);
1475 break;
1476 default:
1477 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1480 if (!hlpfile->lpszTitle)
1481 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
1482 return TRUE;
1485 /***********************************************************************
1487 * HLPFILE_UncompressedLZ77_Size
1489 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1491 int i, newsize = 0;
1493 while (ptr < end)
1495 int mask = *ptr++;
1496 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1498 if (mask & 1)
1500 int code = GET_USHORT(ptr, 0);
1501 int len = 3 + (code >> 12);
1502 newsize += len;
1503 ptr += 2;
1505 else newsize++, ptr++;
1509 return newsize;
1512 /***********************************************************************
1514 * HLPFILE_UncompressLZ77
1516 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1518 int i;
1520 while (ptr < end)
1522 int mask = *ptr++;
1523 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1525 if (mask & 1)
1527 int code = GET_USHORT(ptr, 0);
1528 int len = 3 + (code >> 12);
1529 int offset = code & 0xfff;
1531 * We must copy byte-by-byte here. We cannot use memcpy nor
1532 * memmove here. Just example:
1533 * a[]={1,2,3,4,5,6,7,8,9,10}
1534 * newptr=a+2;
1535 * offset=1;
1536 * We expect:
1537 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
1539 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
1540 ptr += 2;
1542 else *newptr++ = *ptr++;
1546 return newptr;
1549 /***********************************************************************
1551 * HLPFILE_UncompressLZ77_Phrases
1553 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1555 UINT i, num, dec_size;
1556 BYTE *buf, *end;
1558 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1560 num = phrases.num = GET_USHORT(buf, 9);
1561 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1563 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1565 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1566 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1567 if (!phrases.offsets || !phrases.buffer) return FALSE;
1569 for (i = 0; i <= num; i++)
1570 phrases.offsets[i] = GET_USHORT(buf, 0x11 + 2 * i) - 2 * num - 2;
1572 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)phrases.buffer);
1574 hlpfile->hasPhrases = TRUE;
1575 return TRUE;
1578 /***********************************************************************
1580 * HLPFILE_Uncompress_Phrases40
1582 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1584 UINT num;
1585 INT dec_size, cpr_size;
1586 BYTE *buf_idx, *end_idx;
1587 BYTE *buf_phs, *end_phs;
1588 long* ptr, mask = 0;
1589 unsigned int i;
1590 unsigned short bc, n;
1592 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1593 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1595 ptr = (long*)(buf_idx + 9 + 28);
1596 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1597 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1599 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1600 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1601 GET_UINT(buf_idx, 9 + 0),
1602 GET_UINT(buf_idx, 9 + 4),
1603 GET_UINT(buf_idx, 9 + 8),
1604 GET_UINT(buf_idx, 9 + 12),
1605 GET_UINT(buf_idx, 9 + 16),
1606 GET_UINT(buf_idx, 9 + 20),
1607 GET_USHORT(buf_idx, 9 + 24),
1608 GET_USHORT(buf_idx, 9 + 26));
1610 dec_size = GET_UINT(buf_idx, 9 + 12);
1611 cpr_size = GET_UINT(buf_idx, 9 + 16);
1613 if (dec_size != cpr_size &&
1614 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1616 WINE_WARN("size mismatch %u %u\n",
1617 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1618 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1621 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1622 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1623 if (!phrases.offsets || !phrases.buffer) return FALSE;
1625 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1627 phrases.offsets[0] = 0;
1628 for (i = 0; i < num; i++)
1630 for (n = 1; getbit(); n += 1 << bc);
1631 if (getbit()) n++;
1632 if (bc > 1 && getbit()) n += 2;
1633 if (bc > 2 && getbit()) n += 4;
1634 if (bc > 3 && getbit()) n += 8;
1635 if (bc > 4 && getbit()) n += 16;
1636 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1638 #undef getbit
1640 if (dec_size == cpr_size)
1641 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1642 else
1643 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)phrases.buffer);
1645 hlpfile->hasPhrases = FALSE;
1646 return TRUE;
1649 /***********************************************************************
1651 * HLPFILE_Uncompress_Topic
1653 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1655 BYTE *buf, *ptr, *end, *newptr;
1656 unsigned int i, newsize = 0;
1658 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1659 {WINE_WARN("topic0\n"); return FALSE;}
1661 switch (hlpfile->flags & (8|4))
1663 case 8:
1664 WINE_FIXME("Unsupported format\n");
1665 return FALSE;
1666 case 4:
1667 buf += 9;
1668 topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
1670 for (i = 0; i < topic.wMapLen; i++)
1672 ptr = buf + i * 0x1000;
1674 /* I don't know why, it's necessary for printman.hlp */
1675 if (ptr + 0x44 > end) ptr = end - 0x44;
1677 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + 0x1000));
1680 topic.map = HeapAlloc(GetProcessHeap(), 0,
1681 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1682 if (!topic.map) return FALSE;
1683 newptr = (BYTE*)(topic.map + topic.wMapLen);
1684 topic.end = newptr + newsize;
1686 for (i = 0; i < topic.wMapLen; i++)
1688 ptr = buf + i * 0x1000;
1689 if (ptr + 0x44 > end) ptr = end - 0x44;
1691 topic.map[i] = newptr;
1692 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + 0x1000), newptr);
1694 break;
1695 case 0:
1696 /* basically, we need to copy the 0x1000 byte pages (removing the first 0x0C) in
1697 * one single are in memory
1699 #define DST_LEN (0x1000 - 0x0C)
1700 buf += 9;
1701 newsize = end - buf;
1702 /* number of destination pages */
1703 topic.wMapLen = (newsize - 1) / DST_LEN + 1;
1704 topic.map = HeapAlloc(GetProcessHeap(), 0,
1705 topic.wMapLen * (sizeof(topic.map[0]) + DST_LEN));
1706 if (!topic.map) return FALSE;
1707 newptr = (BYTE*)(topic.map + topic.wMapLen);
1708 topic.end = newptr + newsize;
1710 for (i = 0; i < topic.wMapLen; i++)
1712 topic.map[i] = newptr + i * DST_LEN;
1713 memcpy(topic.map[i], buf + i * 0x1000 + 0x0C, DST_LEN);
1715 #undef DST_LEN
1716 break;
1718 return TRUE;
1721 /***********************************************************************
1723 * HLPFILE_Uncompress2
1726 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1728 BYTE *phptr, *phend;
1729 UINT code;
1730 UINT index;
1732 while (ptr < end && newptr < newend)
1734 if (!*ptr || *ptr >= 0x10)
1735 *newptr++ = *ptr++;
1736 else
1738 code = 0x100 * ptr[0] + ptr[1];
1739 index = (code - 0x100) / 2;
1741 phptr = (BYTE*)phrases.buffer + phrases.offsets[index];
1742 phend = (BYTE*)phrases.buffer + phrases.offsets[index + 1];
1744 if (newptr + (phend - phptr) > newend)
1746 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1747 newptr, newend, phend - phptr);
1748 return;
1750 memcpy(newptr, phptr, phend - phptr);
1751 newptr += phend - phptr;
1752 if (code & 1) *newptr++ = ' ';
1754 ptr += 2;
1757 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1760 /******************************************************************
1761 * HLPFILE_Uncompress3
1765 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1766 const BYTE* src, const BYTE* src_end)
1768 unsigned int idx, len;
1770 for (; src < src_end; src++)
1772 if ((*src & 1) == 0)
1774 idx = *src / 2;
1775 if (idx > phrases.num)
1777 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1778 len = 0;
1780 else
1782 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1783 if (dst + len <= dst_end)
1784 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1787 else if ((*src & 0x03) == 0x01)
1789 idx = (*src + 1) * 64;
1790 idx += *++src;
1791 if (idx > phrases.num)
1793 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1794 len = 0;
1796 else
1798 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1799 if (dst + len <= dst_end)
1800 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1803 else if ((*src & 0x07) == 0x03)
1805 len = (*src / 8) + 1;
1806 if (dst + len <= dst_end)
1807 memcpy(dst, src + 1, len);
1808 src += len;
1810 else
1812 len = (*src / 16) + 1;
1813 if (dst + len <= dst_end)
1814 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1816 dst += len;
1819 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1820 return TRUE;
1823 /******************************************************************
1824 * HLPFILE_UncompressRLE
1828 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1830 BYTE ch;
1831 BYTE* sdst = *dst + dstsz;
1833 while (src < end)
1835 ch = *src++;
1836 if (ch & 0x80)
1838 ch &= 0x7F;
1839 if ((*dst) + ch <= sdst)
1840 memcpy(*dst, src, ch);
1841 src += ch;
1843 else
1845 if ((*dst) + ch <= sdst)
1846 memset(*dst, (char)*src, ch);
1847 src++;
1849 *dst += ch;
1851 if (*dst != sdst)
1852 WINE_WARN("Buffer X-flow: d(%u) instead of d(%u)\n",
1853 *dst - (sdst - dstsz), dstsz);
1856 /******************************************************************
1857 * HLPFILE_EnumBTreeLeaves
1861 static void HLPFILE_EnumBTreeLeaves(const BYTE* buf, const BYTE* end, unsigned (*fn)(const BYTE*, void*), void* user)
1863 unsigned psize, pnext;
1864 unsigned num, nlvl;
1865 const BYTE* ptr;
1867 num = GET_UINT(buf, 9 + 34);
1868 psize = GET_USHORT(buf, 9 + 4);
1869 nlvl = GET_USHORT(buf, 9 + 32);
1870 pnext = GET_USHORT(buf, 9 + 26);
1872 WINE_TRACE("BTree: #entries=%u pagSize=%u #levels=%u #pages=%u root=%u struct%16s\n",
1873 num, psize, nlvl, GET_USHORT(buf, 9 + 30), pnext, buf + 9 + 6);
1874 if (!num) return;
1876 while (--nlvl > 0)
1878 ptr = (buf + 9 + 38) + pnext * psize;
1879 WINE_TRACE("BTree: (index[%u]) unused=%u #entries=%u <%u\n",
1880 pnext, GET_USHORT(ptr, 0), GET_USHORT(ptr, 2), GET_USHORT(ptr, 4));
1881 pnext = GET_USHORT(ptr, 4);
1883 while (pnext != 0xFFFF)
1885 const BYTE* node_page;
1886 unsigned short limit;
1888 node_page = ptr = (buf + 9 + 38) + pnext * psize;
1889 limit = GET_USHORT(ptr, 2);
1890 WINE_TRACE("BTree: (leaf [%u]) unused=%u #entries=%u <%u >%u\n",
1891 pnext, GET_USHORT(ptr, 0), limit, GET_USHORT(ptr, 4), GET_USHORT(ptr, 6));
1892 ptr += 8;
1893 while (limit--)
1894 ptr += (fn)(ptr, user);
1895 pnext = GET_USHORT(node_page, 6);
1899 struct myfncb {
1900 HLPFILE* hlpfile;
1901 int i;
1904 static unsigned myfn(const BYTE* ptr, void* user)
1906 struct myfncb* m = user;
1908 m->hlpfile->Context[m->i].lHash = GET_UINT(ptr, 0);
1909 m->hlpfile->Context[m->i].offset = GET_UINT(ptr, 4);
1910 m->i++;
1911 return 8;
1914 /***********************************************************************
1916 * HLPFILE_GetContext
1918 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
1920 BYTE *cbuf, *cend;
1921 struct myfncb m;
1922 unsigned clen;
1924 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
1926 clen = GET_UINT(cbuf, 0x2b);
1927 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen * sizeof(HLPFILE_CONTEXT));
1928 if (!hlpfile->Context) return FALSE;
1929 hlpfile->wContextLen = clen;
1931 m.hlpfile = hlpfile;
1932 m.i = 0;
1933 HLPFILE_EnumBTreeLeaves(cbuf, cend, myfn, &m);
1935 return TRUE;
1938 /***********************************************************************
1940 * HLPFILE_GetMap
1942 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
1944 BYTE *cbuf, *cend;
1945 unsigned entries, i;
1947 if (!HLPFILE_FindSubFile("|CTXOMAP", &cbuf, &cend)) {WINE_WARN("no map section\n"); return FALSE;}
1949 entries = GET_USHORT(cbuf, 9);
1950 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
1951 if (!hlpfile->Map) return FALSE;
1952 hlpfile->wMapLen = entries;
1953 for (i = 0; i < entries; i++)
1955 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
1956 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
1958 return TRUE;
1961 /******************************************************************
1962 * HLPFILE_DeleteLink
1966 void HLPFILE_FreeLink(HLPFILE_LINK* link)
1968 if (link && !--link->wRefCount)
1969 HeapFree(GetProcessHeap(), 0, link);
1972 /***********************************************************************
1974 * HLPFILE_DeleteParagraph
1976 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
1978 HLPFILE_PARAGRAPH* next;
1980 while (paragraph)
1982 next = paragraph->next;
1984 if (paragraph->cookie == para_metafile)
1985 DeleteMetaFile(paragraph->u.gfx.u.mfp.hMF);
1987 HLPFILE_FreeLink(paragraph->link);
1989 HeapFree(GetProcessHeap(), 0, paragraph);
1990 paragraph = next;
1994 /***********************************************************************
1996 * DeleteMacro
1998 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
2000 HLPFILE_MACRO* next;
2002 while (macro)
2004 next = macro->next;
2005 HeapFree(GetProcessHeap(), 0, macro);
2006 macro = next;
2010 /***********************************************************************
2012 * DeletePage
2014 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
2016 HLPFILE_PAGE* next;
2018 while (page)
2020 next = page->next;
2021 HLPFILE_DeleteParagraph(page->first_paragraph);
2022 HLPFILE_DeleteMacro(page->first_macro);
2023 HeapFree(GetProcessHeap(), 0, page);
2024 page = next;
2028 /***********************************************************************
2030 * HLPFILE_FreeHlpFile
2032 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
2034 unsigned i;
2036 if (!hlpfile || --hlpfile->wRefCount > 0) return;
2038 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
2039 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
2040 else first_hlpfile = hlpfile->next;
2042 if (hlpfile->numFonts)
2044 for (i = 0; i < hlpfile->numFonts; i++)
2046 DeleteObject(hlpfile->fonts[i].hFont);
2048 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
2051 if (hlpfile->numBmps)
2053 for (i = 0; i < hlpfile->numBmps; i++)
2055 DeleteObject(hlpfile->bmps[i]);
2057 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
2060 HLPFILE_DeletePage(hlpfile->first_page);
2061 HLPFILE_DeleteMacro(hlpfile->first_macro);
2063 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
2064 HeapFree(GetProcessHeap(), 0, hlpfile->Context);
2065 HeapFree(GetProcessHeap(), 0, hlpfile->Map);
2066 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
2067 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
2068 HeapFree(GetProcessHeap(), 0, hlpfile);