Added Dutch translation.
[wine] / programs / winhelp / hlpfile.c
1 /*
2  * Help Viewer
3  *
4  * Copyright    1996 Ulrich Schmid
5  *              2002 Eric Pouech
6  *
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.
11  *
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.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winhelp.h"
31
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
35
36 static inline unsigned short GET_USHORT(const BYTE* buffer, unsigned i)
37 {
38     return (BYTE)buffer[i] + 0x100 * (BYTE)buffer[i + 1];
39 }
40
41 static inline short GET_SHORT(const BYTE* buffer, unsigned i)
42 {
43     return (BYTE)buffer[i] + 0x100 * (signed char)buffer[i+1];
44 }
45
46 static inline unsigned GET_UINT(const BYTE* buffer, unsigned i)
47 {
48     return GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i + 2);
49 }
50
51 static HLPFILE *first_hlpfile = 0;
52 static BYTE    *file_buffer;
53
54 static struct
55 {
56     UINT        num;
57     unsigned*   offsets;
58     char*       buffer;
59 } phrases;
60
61 static struct
62 {
63     BYTE**      map;
64     BYTE*       end;
65     UINT        wMapLen;
66 } topic;
67
68 static struct
69 {
70     UINT                wFont;
71     UINT                wIndent;
72     UINT                wHSpace;
73     UINT                wVSpace;
74     HLPFILE_LINK*       link;
75 } attributes;
76
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_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
88 static BOOL  HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
89 static void  HLPFILE_Uncompress2(const BYTE*, const BYTE*, BYTE*, const BYTE*);
90 static BOOL  HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
91 static void  HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz);
92 static BOOL  HLPFILE_ReadFont(HLPFILE* hlpfile);
93
94 /***********************************************************************
95  *
96  *           HLPFILE_PageByNumber
97  */
98 HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
99 {
100     HLPFILE_PAGE *page;
101     HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
102
103     if (!hlpfile) return 0;
104
105     WINE_TRACE("[%s/%u]\n", lpszPath, wNum);
106
107     for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
108
109     /* HLPFILE_FreeHlpFile(lpszPath); */
110
111     return page;
112 }
113
114 /* FIXME:
115  * this finds the page containing the offset. The offset can either
116  * refer to the top of the page (offset == page->offset), or
117  * to some paragraph inside the page...
118  * As of today, we only return the page... we should also return
119  * a paragraph, and then, while opening a new page, compute the
120  * y-offset of the paragraph to be shown and scroll the window
121  * accordinly
122  */
123 /******************************************************************
124  *              HLPFILE_PageByOffset
125  *
126  *
127  */
128 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
129 {
130     HLPFILE_PAGE*       page;
131     HLPFILE_PAGE*       found;
132
133     if (!hlpfile) return 0;
134
135     WINE_TRACE("<%s>[%lx]\n", hlpfile->lpszPath, offset);
136
137     if (offset == 0xFFFFFFFF) return NULL;
138     page = NULL;
139
140     for (found = NULL, page = hlpfile->first_page; page; page = page->next)
141     {
142         if (page->offset <= offset && (!found || found->offset < page->offset))
143             found = page;
144     }
145     if (!found)
146         WINE_ERR("Page of offset %lu not found in file %s\n",
147                  offset, hlpfile->lpszPath);
148     return found;
149 }
150
151 /***********************************************************************
152  *
153  *           HLPFILE_HlpFilePageByHash
154  */
155 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
156 {
157     int                 i;
158
159     if (!hlpfile) return 0;
160
161     WINE_TRACE("<%s>[%lx]\n", hlpfile->lpszPath, lHash);
162
163     for (i = 0; i < hlpfile->wContextLen; i++)
164     {
165         if (hlpfile->Context[i].lHash == lHash)
166             return HLPFILE_PageByOffset(hlpfile, hlpfile->Context[i].offset);
167     }
168
169     WINE_ERR("Page of hash %lx not found in file %s\n", lHash, hlpfile->lpszPath);
170     return NULL;
171 }
172
173 /***********************************************************************
174  *
175  *           HLPFILE_Contents
176  */
177 HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
178 {
179     HLPFILE_PAGE*       page = NULL;
180
181     if (!hlpfile) return NULL;
182
183     page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
184     if (!page) page = hlpfile->first_page;
185     return page;
186 }
187
188 /***********************************************************************
189  *
190  *           HLPFILE_Hash
191  */
192 LONG HLPFILE_Hash(LPCSTR lpszContext)
193 {
194     LONG lHash = 0;
195     CHAR c;
196
197     while ((c = *lpszContext++))
198     {
199         CHAR x = 0;
200         if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
201         if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
202         if (c >= '1' && c <= '9') x = c - '0';
203         if (c == '0') x = 10;
204         if (c == '.') x = 12;
205         if (c == '_') x = 13;
206         if (x) lHash = lHash * 43 + x;
207     }
208     return lHash;
209 }
210
211 /***********************************************************************
212  *
213  *           HLPFILE_ReadHlpFile
214  */
215 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
216 {
217     HLPFILE*      hlpfile;
218
219     for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
220     {
221         if (!strcmp(lpszPath, hlpfile->lpszPath))
222         {
223             hlpfile->wRefCount++;
224             return hlpfile;
225         }
226     }
227
228     hlpfile = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
229     if (!hlpfile) return 0;
230
231     hlpfile->lpszPath           = (char*)hlpfile + sizeof(HLPFILE);
232     hlpfile->lpszTitle          = NULL;
233     hlpfile->lpszCopyright      = NULL;
234     hlpfile->first_page         = NULL;
235     hlpfile->first_macro        = NULL;
236     hlpfile->wContextLen        = 0;
237     hlpfile->Context            = NULL;
238     hlpfile->contents_start     = 0xFFFFFFFF;
239     hlpfile->prev               = NULL;
240     hlpfile->next               = first_hlpfile;
241     hlpfile->wRefCount          = 1;
242
243     hlpfile->numBmps            = 0;
244     hlpfile->bmps               = NULL;
245
246     hlpfile->numFonts           = 0;
247     hlpfile->fonts              = NULL;
248
249     hlpfile->numWindows         = 0;
250     hlpfile->windows            = NULL;
251
252     strcpy(hlpfile->lpszPath, lpszPath);
253
254     first_hlpfile = hlpfile;
255     if (hlpfile->next) hlpfile->next->prev = hlpfile;
256
257     phrases.offsets = NULL;
258     phrases.buffer = NULL;
259     topic.map = NULL;
260     topic.end = NULL;
261     file_buffer = NULL;
262
263     if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
264     {
265         HLPFILE_FreeHlpFile(hlpfile);
266         hlpfile = 0;
267     }
268
269     if (phrases.offsets)  HeapFree(GetProcessHeap(), 0, phrases.offsets);
270     if (phrases.buffer)   HeapFree(GetProcessHeap(), 0, phrases.buffer);
271     if (topic.map)        HeapFree(GetProcessHeap(), 0, topic.map);
272     if (file_buffer)      HeapFree(GetProcessHeap(), 0, file_buffer);
273
274     return hlpfile;
275 }
276
277 /***********************************************************************
278  *
279  *           HLPFILE_DoReadHlpFile
280  */
281 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
282 {
283     BOOL        ret;
284     HFILE       hFile;
285     OFSTRUCT    ofs;
286     BYTE*       buf;
287     DWORD       ref = 0x0C;
288     unsigned    index, old_index, offset, len, offs;
289
290     hFile = OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH);
291     if (hFile == HFILE_ERROR) return FALSE;
292
293     ret = HLPFILE_ReadFileToBuffer(hFile);
294     _lclose(hFile);
295     if (!ret) return FALSE;
296
297     if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
298
299     /* load phrases support */
300     if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
301         HLPFILE_Uncompress_Phrases40(hlpfile);
302
303     if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
304     if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
305
306     buf = topic.map[0];
307     old_index = -1;
308     offs = 0;
309     do
310     {
311         BYTE*   end;
312
313         /* FIXME this depends on the blocksize, can be 2k in some cases */
314         index  = (ref - 0x0C) >> 14;
315         offset = (ref - 0x0C) & 0x3fff;
316
317         WINE_TRACE("ref=%08lx => [%u/%u]\n", ref, index, offset);
318
319         if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
320         buf = topic.map[index] + offset;
321         if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
322         end = min(buf + GET_UINT(buf, 0), topic.end);
323         if (index != old_index) {offs = 0; old_index = index;}
324
325         switch (buf[0x14])
326         {
327         case 0x02:
328             if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
329             break;
330
331         case 0x20:
332             if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
333             offs += len;
334             break;
335
336         case 0x23:
337             if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
338             offs += len;
339             break;
340
341         default:
342             WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
343         }
344
345         ref = GET_UINT(buf, 0xc);
346     } while (ref != 0xffffffff);
347
348     return HLPFILE_GetContext(hlpfile);
349 }
350
351 /***********************************************************************
352  *
353  *           HLPFILE_AddPage
354  */
355 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
356 {
357     HLPFILE_PAGE* page;
358     BYTE*         title;
359     UINT          titlesize;
360     char*         ptr;
361     HLPFILE_MACRO*macro;
362
363     if (buf + 0x31 > end) {WINE_WARN("page1\n"); return FALSE;};
364     title = buf + GET_UINT(buf, 0x10);
365     if (title > end) {WINE_WARN("page2\n"); return FALSE;};
366
367     titlesize = GET_UINT(buf, 4);
368     page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
369     if (!page) return FALSE;
370     page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
371
372     if (hlpfile->hasPhrases)
373     {
374         HLPFILE_Uncompress2(title, end, page->lpszTitle, page->lpszTitle + titlesize);
375     }
376     else
377     {
378         if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
379         {
380             /* need to decompress */
381             HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize, 
382                                 title, end);
383         }
384         else
385         {
386             memcpy(page->lpszTitle, title, titlesize);
387         }
388     }
389
390     page->lpszTitle[titlesize] = '\0';
391
392     if (hlpfile->first_page)
393     {
394         HLPFILE_PAGE  *p;
395
396         for (p = hlpfile->first_page; p->next; p = p->next);
397         page->prev = p;
398         p->next    = page;
399     }
400     else
401     {
402         hlpfile->first_page = page;
403         page->prev = NULL;
404     }
405
406     page->file            = hlpfile;
407     page->next            = NULL;
408     page->first_paragraph = NULL;
409     page->first_macro     = NULL;
410     page->wNumber         = GET_UINT(buf, 0x21);
411     page->offset          = offset;
412
413     page->browse_bwd = GET_UINT(buf, 0x19);
414     page->browse_fwd = GET_UINT(buf, 0x1D);
415
416     WINE_TRACE("Added page[%d]: title='%s' %08lx << %08x >> %08lx\n",
417                page->wNumber, page->lpszTitle, 
418                page->browse_bwd, page->offset, page->browse_fwd);
419
420     memset(&attributes, 0, sizeof(attributes));
421
422     /* now load macros */
423     ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
424     while (ptr < page->lpszTitle + titlesize)
425     {
426         unsigned len = strlen(ptr);
427         WINE_TRACE("macro: %s\n", ptr);
428         macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
429         macro->lpszMacro = (char*)(macro + 1);
430         memcpy((char*)macro->lpszMacro, ptr, len + 1);
431         /* FIXME: shall we really link macro in reverse order ??
432          * may produce strange results when played at page opening
433          */
434         macro->next = page->first_macro;
435         page->first_macro = macro;
436         ptr += len + 1;
437     }
438
439     return TRUE;
440 }
441
442 static long fetch_long(BYTE** ptr)
443 {
444     long        ret;
445
446     if (*(*ptr) & 1)
447     {
448         ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
449         (*ptr) += 4;
450     }
451     else
452     {
453         ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
454         (*ptr) += 2;
455     }
456
457     return ret;
458 }
459
460 static unsigned long fetch_ulong(BYTE** ptr)
461 {
462     unsigned long        ret;
463
464     if (*(*ptr) & 1)
465     {
466         ret = *(unsigned long*)(*ptr) / 2;
467         (*ptr) += 4;
468     }
469     else
470     {
471         ret = *(unsigned short*)(*ptr) / 2;
472         (*ptr) += 2;
473     }
474     return ret;
475 }    
476
477 static short fetch_short(BYTE** ptr)
478 {
479     short       ret;
480
481     if (*(*ptr) & 1)
482     {
483         ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
484         (*ptr) += 2;
485     }
486     else
487     {
488         ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
489         (*ptr)++;
490     }
491     return ret;
492 }
493
494 static unsigned short fetch_ushort(BYTE** ptr)
495 {
496     unsigned short ret;
497
498     if (*(*ptr) & 1)
499     {
500         ret = *(unsigned short*)(*ptr) / 2;
501         (*ptr) += 2;
502     }
503     else
504     {
505         ret = *(unsigned char*)(*ptr) / 2;
506         (*ptr)++;
507     }
508     return ret;
509 }
510
511 /******************************************************************
512  *              HLPFILE_DecompressGfx
513  *
514  * Decompress the data part of a bitmap or a metafile
515  */
516 static BYTE*    HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE packing)
517 {
518     BYTE*       dst;
519     BYTE*       tmp;
520     BYTE*       tmp2;
521     unsigned    sz77;
522
523     WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
524
525     switch (packing)
526     {
527     case 0: /* uncompressed */
528         if (sz != csz)
529             WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
530         dst = src;
531         break;
532     case 1: /* RunLen */
533         tmp = dst = HeapAlloc(GetProcessHeap(), 0, sz);
534         if (!dst) return NULL;
535         HLPFILE_UncompressRLE(src, src + csz, &tmp, sz);
536         if (tmp - dst != sz)
537             WINE_FIXME("Bogus gfx sizes (RunLen): %u/%u\n", tmp - dst, sz);
538         break;
539     case 2: /* LZ77 */
540         sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
541         dst = HeapAlloc(GetProcessHeap(), 0, sz77);
542         if (!dst) return NULL;
543         HLPFILE_UncompressLZ77(src, src + csz, dst);
544         if (sz77 != sz)
545             WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
546         break;
547     case 3: /* LZ77 then RLE */
548         sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
549         tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
550         if (!tmp) return FALSE;
551         HLPFILE_UncompressLZ77(src, src + csz, tmp);
552         dst = tmp2 = HeapAlloc(GetProcessHeap(), 0, sz);
553         if (!dst) return FALSE;
554         HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
555         if (tmp2 - dst != sz)
556             WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
557         HeapFree(GetProcessHeap(), 0, tmp);
558         break;
559     default:
560         WINE_FIXME("Unsupported packing %u\n", packing);
561         return NULL;
562     }
563     return dst;
564 }
565
566 /******************************************************************
567  *              HLPFILE_LoadBitmap
568  *
569  *
570  */
571 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack, 
572                                HLPFILE_PARAGRAPH* paragraph)
573 {
574     BYTE*               ptr;
575     BYTE*               pict_beg;
576     BITMAPINFO*         bi;
577     unsigned long       off, csz;
578     HDC                 hdc;
579
580     bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
581     if (!bi) return FALSE;
582
583     ptr = beg + 2; /* for type and pack */
584
585     bi->bmiHeader.biSize          = sizeof(bi->bmiHeader);
586     bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
587     bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
588     bi->bmiHeader.biPlanes        = fetch_ushort(&ptr);
589     bi->bmiHeader.biBitCount      = fetch_ushort(&ptr);
590     bi->bmiHeader.biWidth         = fetch_ulong(&ptr);
591     bi->bmiHeader.biHeight        = fetch_ulong(&ptr);
592     bi->bmiHeader.biClrUsed       = fetch_ulong(&ptr);
593     bi->bmiHeader.biClrImportant  = fetch_ulong(&ptr);
594     bi->bmiHeader.biCompression   = BI_RGB;
595     if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
596     if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
597     bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
598     WINE_TRACE("planes=%d bc=%d size=(%ld,%ld)\n",
599                bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount, 
600                bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
601
602     csz = fetch_ulong(&ptr);
603     fetch_ulong(&ptr); /* hotspot size */
604
605     off = GET_UINT(ptr, 0);     ptr += 4;
606     /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
607     
608     /* now read palette info */
609     if (type == 0x06)
610     {
611         unsigned nc = bi->bmiHeader.biClrUsed;
612         unsigned i;
613         
614         /* not quite right, especially for bitfields type of compression */
615         if (!nc && bi->bmiHeader.biBitCount <= 8)
616             nc = 1 << bi->bmiHeader.biBitCount;
617         
618         bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
619         if (!bi) return FALSE;
620         for (i = 0; i < nc; i++)
621         {
622             bi->bmiColors[i].rgbBlue     = ptr[0];
623             bi->bmiColors[i].rgbGreen    = ptr[1];
624             bi->bmiColors[i].rgbRed      = ptr[2];
625             bi->bmiColors[i].rgbReserved = 0;
626             ptr += 4;
627         }
628     }
629     pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
630     
631     paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader, 
632                                                     CBM_INIT, pict_beg, 
633                                                     bi, DIB_RGB_COLORS);
634     ReleaseDC(0, hdc);      
635     if (!paragraph->u.gfx.u.bmp.hBitmap)
636         WINE_ERR("Couldn't create bitmap\n");
637     
638     HeapFree(GetProcessHeap(), 0, bi);
639     if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
640
641     return TRUE;
642 }
643
644 /******************************************************************
645  *              HLPFILE_LoadMetaFile
646  *
647  *
648  */
649 static BOOL     HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
650 {
651     BYTE*               ptr;
652     unsigned long       size, csize;
653     unsigned long       off, hsoff;
654     BYTE*               bits;
655     METAFILEPICT        mfp;
656
657     WINE_TRACE("Loading metafile\n");
658
659     ptr = beg + 2; /* for type and pack */
660
661     mfp.mm = fetch_ushort(&ptr); /* mapping mode */
662
663     mfp.xExt = GET_USHORT(ptr, 0);
664     mfp.yExt = GET_USHORT(ptr, 2);
665     ptr += 4;
666
667     size = fetch_ulong(&ptr); /* decompressed size */
668     csize = fetch_ulong(&ptr); /* compressed size */
669     fetch_ulong(&ptr); /* hotspot size */
670     off = GET_UINT(ptr, 0);
671     hsoff = GET_UINT(ptr, 4);
672     ptr += 8;
673
674     WINE_FIXME("sz=%lu csz=%lu (%ld,%ld) offs=%lu/%u,%lu\n", 
675                size, csize, mfp.xExt, mfp.yExt, off, ptr - beg, hsoff);
676
677     bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
678     if (!bits) return FALSE;
679
680     paragraph->cookie = para_metafile;
681
682     mfp.hMF = NULL;
683
684     paragraph->u.gfx.u.mf.hMetaFile = SetMetaFileBitsEx(size, bits);
685
686     if (!paragraph->u.gfx.u.mf.hMetaFile)
687         WINE_FIXME("Couldn't load metafile\n");
688
689     if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
690
691     paragraph->u.gfx.u.mf.mfSize.cx = mfp.xExt;
692     paragraph->u.gfx.u.mf.mfSize.cy = mfp.yExt;
693
694     return TRUE;
695 }
696
697 /******************************************************************
698  *              HLPFILE_LoadGfxByAddr
699  *
700  *
701  */
702 static  BOOL    HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
703                                       unsigned long size, 
704                                       HLPFILE_PARAGRAPH* paragraph)
705 {
706     unsigned    i, numpict;
707
708     numpict = GET_USHORT(ref, 2);
709     WINE_TRACE("Got picture magic=%04x #=%d\n", 
710                GET_USHORT(ref, 0), numpict);
711
712     for (i = 0; i < numpict; i++)
713     {
714         BYTE*   beg;
715         BYTE*   ptr;
716         BYTE    type, pack;
717
718         WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
719         beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
720
721         type = *ptr++;
722         pack = *ptr++;
723         
724         switch (type)
725         {
726         case 5: /* device dependent bmp */
727         case 6: /* device independent bmp */
728             HLPFILE_LoadBitmap(beg, type, pack, paragraph);
729             break;
730         case 8: 
731             HLPFILE_LoadMetaFile(beg, pack, paragraph);
732             break;
733         default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
734         }
735
736         /* FIXME: hotspots */
737
738         /* FIXME: implement support for multiple picture format */
739         if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
740         break;
741     }
742     return TRUE;
743 }
744
745 /******************************************************************
746  *              HLPFILE_LoadGfxByIndex
747  *
748  *
749  */
750 static  BOOL    HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index, 
751                                        HLPFILE_PARAGRAPH* paragraph)
752 {
753     char        tmp[16];
754     BYTE        *ref, *end;
755     BOOL        ret;
756
757     WINE_TRACE("Loading picture #%d\n", index);
758
759     if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
760     {
761         paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
762         return TRUE;
763     }
764
765     sprintf(tmp, "|bm%u", index);
766
767     if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
768
769     ref += 9;
770
771     ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
772
773     /* cache bitmap */
774     if (ret && paragraph->cookie == para_bitmap)
775     {
776         if (index >= hlpfile->numBmps)
777         {
778             hlpfile->numBmps = index + 1;
779             hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps, 
780                                         hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
781         }
782         hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
783     }
784     return ret;
785 }
786
787 /******************************************************************
788  *              HLPFILE_AllocLink
789  *
790  *
791  */
792 static HLPFILE_LINK*       HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
793                                              BOOL clrChange, unsigned wnd)
794 {
795     HLPFILE_LINK*  link;
796
797     /* FIXME: should build a string table for the attributes.link.lpszPath
798      * they are reallocated for each link
799      */
800     link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
801     if (!link) return NULL;
802
803     link->cookie     = cookie;
804     link->lpszString = (char*)link + sizeof(HLPFILE_LINK);
805     strcpy((char*)link->lpszString, str);
806     link->lHash      = hash;
807     link->bClrChange = clrChange ? 1 : 0;
808     link->window     = wnd;
809     link->wRefCount   = 1;
810
811     WINE_TRACE("Link[%d] to %s@%08lx:%d\n",
812                link->cookie, link->lpszString, 
813                link->lHash, link->window);
814     return link;
815 }
816
817 /***********************************************************************
818  *
819  *           HLPFILE_AddParagraph
820  */
821 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
822 {
823     HLPFILE_PAGE      *page;
824     HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
825     UINT               textsize;
826     BYTE              *format, *format_end, *text, *text_end;
827     long               size;
828     unsigned short     bits;
829     unsigned           nc, ncol = 1;
830
831     if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
832
833     for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
834     for (paragraphptr = &page->first_paragraph; *paragraphptr;
835          paragraphptr = &(*paragraphptr)->next) /* Nothing */;
836
837     if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
838
839     size = GET_UINT(buf, 0x4);
840     text = HeapAlloc(GetProcessHeap(), 0, size);
841     if (!text) return FALSE;
842     if (hlpfile->hasPhrases)
843     {
844         HLPFILE_Uncompress2(buf + GET_UINT(buf, 0x10), end, text, text + size);
845     }
846     else
847     {
848         if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
849         {
850             /* block is compressed */
851             HLPFILE_Uncompress3(text, text + size, buf + GET_UINT(buf, 0x10), end);
852         }
853         else
854         {
855             text = buf + GET_UINT(buf, 0x10);
856         }
857     }
858     text_end = text + size;
859
860     format = buf + 0x15;
861     format_end = buf + GET_UINT(buf, 0x10);
862
863     fetch_long(&format);
864     *len = fetch_ushort(&format);
865
866     if (buf[0x14] == 0x23)
867     {
868         char    type;
869
870         ncol = *format++;
871
872         WINE_TRACE("#cols %u\n", ncol);
873         type = *format++;
874         if (type == 0 || type == 2)
875             format += 2;
876         format += ncol * 4;
877     }
878
879     for (nc = 0; nc < ncol; nc++)
880     {
881         WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
882         if (buf[0x14] == 0x23)
883             format += 5;
884         format += 4;
885         bits = GET_USHORT(format, 0); format += 2;
886         if (bits & 0x0001) fetch_long(&format);
887         if (bits & 0x0002) fetch_short(&format);
888         if (bits & 0x0004) fetch_short(&format);
889         if (bits & 0x0008) fetch_short(&format);
890         if (bits & 0x0010) fetch_short(&format);
891         if (bits & 0x0020) fetch_short(&format);
892         if (bits & 0x0040) fetch_short(&format);
893         if (bits & 0x0100) format += 3;
894         if (bits & 0x0200)
895         {
896             int                 ntab = fetch_short(&format);
897             unsigned short      ts;
898
899             while (ntab-- > 0)
900             {
901                 ts = fetch_ushort(&format);
902                 if (ts & 0x4000) fetch_ushort(&format);
903             }
904         }
905         /* 0x0400, 0x0800 and 0x1000 don't need space */
906         if ((bits & 0xE080) != 0) 
907             WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
908
909         while (text < text_end && format < format_end)
910         {
911             WINE_TRACE("Got text: '%s' (%p/%p - %p/%p)\n", text, text, text_end, format, format_end);
912             textsize = strlen(text) + 1;
913             if (textsize > 1)
914             {
915                 paragraph = HeapAlloc(GetProcessHeap(), 0,
916                                       sizeof(HLPFILE_PARAGRAPH) + textsize);
917                 if (!paragraph) return FALSE;
918                 *paragraphptr = paragraph;
919                 paragraphptr = &paragraph->next;
920
921                 paragraph->next            = NULL;
922                 paragraph->link            = attributes.link;
923                 if (paragraph->link) paragraph->link->wRefCount++;
924                 paragraph->cookie          = para_normal_text;
925                 paragraph->u.text.wFont    = attributes.wFont;
926                 paragraph->u.text.wVSpace  = attributes.wVSpace;
927                 paragraph->u.text.wHSpace  = attributes.wHSpace;
928                 paragraph->u.text.wIndent  = attributes.wIndent;
929                 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
930                 strcpy(paragraph->u.text.lpszText, text);
931
932                 attributes.wVSpace = 0;
933                 attributes.wHSpace = 0;
934             }
935             /* else: null text, keep on storing attributes */
936             text += textsize;
937
938             if (*format == 0xff)
939             {
940                 format++;
941                 break;
942             }
943
944             WINE_TRACE("format=%02x\n", *format);
945             switch (*format)
946             {
947             case 0x20:
948                 WINE_FIXME("NIY20\n");
949                 format += 5;
950                 break;
951
952             case 0x21:
953                 WINE_FIXME("NIY21\n");
954                 format += 3;
955                 break;
956
957             case 0x80:
958                 attributes.wFont = GET_USHORT(format, 1);
959                 WINE_TRACE("Changing font to %d\n", attributes.wFont);
960                 format += 3;
961                 break;
962
963             case 0x81:
964                 attributes.wVSpace++;
965                 format += 1;
966                 break;
967
968             case 0x82:
969                 attributes.wVSpace++;
970                 attributes.wIndent = 0;
971                 format += 1;
972                 break;
973
974             case 0x83:
975                 attributes.wIndent++;
976                 format += 1;
977                 break;
978
979 #if 0
980             case 0x84:
981                 format += 3;
982                 break;
983 #endif
984
985             case 0x86:
986             case 0x87:
987             case 0x88:
988                 {
989                     BYTE    pos = (*format - 0x86);
990                     BYTE    type = format[1];
991                     long    size;
992
993                     format += 2;
994                     size = fetch_long(&format);
995
996                     paragraph = HeapAlloc(GetProcessHeap(), 0,
997                                           sizeof(HLPFILE_PARAGRAPH) + textsize);
998                     if (!paragraph) return FALSE;
999                     *paragraphptr = paragraph;
1000                     paragraphptr = &paragraph->next;
1001
1002                     paragraph->next        = NULL;
1003                     paragraph->link        = attributes.link;
1004                     if (paragraph->link) paragraph->link->wRefCount++;
1005                     paragraph->cookie      = para_bitmap;
1006                     paragraph->u.gfx.pos   = pos;
1007                     switch (type)
1008                     {
1009                     case 0x22:
1010                         fetch_ushort(&format); /* hot spot */
1011                         /* fall thru */
1012                     case 0x03:
1013                         switch (GET_SHORT(format, 0))
1014                         {
1015                         case 0:
1016                             HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2), 
1017                                                    paragraph);
1018                             break;
1019                         case 1:
1020                             WINE_FIXME("does it work ??? %x<%lu>#%u\n", 
1021                                        GET_SHORT(format, 0), 
1022                                        size, GET_SHORT(format, 2));
1023                             HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4, 
1024                                                   paragraph);
1025                             break;
1026                         default:
1027                             WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1028                             break;
1029                         }
1030                         break;
1031                     case 0x05:
1032                         WINE_FIXME("Got an embedded element %s\n", format + 6);
1033                         break;
1034                     default:
1035                         WINE_FIXME("Got a type %d picture\n", type);
1036                         break;
1037                     }
1038                     if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1039
1040                     format += size;
1041                 }
1042                 break;
1043
1044             case 0x89:
1045                 HLPFILE_FreeLink(attributes.link);
1046                 attributes.link = NULL;
1047                 format += 1;
1048                 break;
1049
1050             case 0x8B:
1051             case 0x8C:
1052                 WINE_FIXME("NIY non-break space/hyphen\n");
1053                 format += 1;
1054                 break;
1055
1056 #if 0
1057             case 0xA9:
1058                 format += 2;
1059                 break;
1060 #endif
1061
1062             case 0xC8:
1063             case 0xCC:
1064                 WINE_TRACE("macro => %s\n", format + 3);
1065                 HLPFILE_FreeLink(attributes.link);
1066                 attributes.link = HLPFILE_AllocLink(hlp_link_macro, format + 3, 
1067                                                     0, !(*format & 4), -1);
1068                 format += 3 + GET_USHORT(format, 1);
1069                 break;
1070
1071             case 0xE0:
1072             case 0xE1:
1073                 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1074                 format += 5;
1075                 break;
1076
1077             case 0xE2:
1078             case 0xE3:
1079             case 0xE6:
1080             case 0xE7:
1081                 HLPFILE_FreeLink(attributes.link);
1082                 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1083                                                     hlpfile->lpszPath, 
1084                                                     GET_UINT(format, 1), 
1085                                                     !(*format & 4), -1);
1086                 format += 5;
1087                 break;
1088
1089             case 0xEA:
1090             case 0xEB:
1091             case 0xEE:
1092             case 0xEF:
1093                 {
1094                     char*       ptr = format + 8;
1095                     BYTE        type = format[3];
1096                     int         wnd = -1;
1097                     char*       str;
1098
1099                     if (type == 1) wnd = *ptr++;
1100                     if (type == 4 || type == 6)
1101                     {
1102                         str = ptr;
1103                         ptr += strlen(ptr) + 1;
1104                     }
1105                     else
1106                         str = hlpfile->lpszPath;
1107                     if (type == 6)
1108                     {
1109                         for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1110                         {
1111                             if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1112                         }
1113                         if (wnd == -1)
1114                             WINE_WARN("Couldn't find window info for %s\n", ptr);
1115                     }
1116                     HLPFILE_FreeLink(attributes.link);
1117                     attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1118                                                         str, GET_UINT(format, 4),
1119                                                         !(*format & 1), wnd);
1120                 }
1121                 format += 3 + GET_USHORT(format, 1);
1122                 break;
1123
1124             default:
1125                 WINE_WARN("format %02x\n", *format);
1126                 format++;
1127             }
1128         }
1129     }
1130     if (text_end != buf + GET_UINT(buf, 0x10) + size)
1131         HeapFree(GetProcessHeap(), 0, text_end - size);
1132     return TRUE;
1133 }
1134
1135 /******************************************************************
1136  *              HLPFILE_ReadFont
1137  *
1138  *
1139  */
1140 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1141 {
1142     BYTE        *ref, *end;
1143     unsigned    i, len, idx;
1144     unsigned    face_num, dscr_num, face_offset, dscr_offset;
1145     BYTE        flag, family;
1146
1147     if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1148     {
1149         WINE_WARN("no subfile FONT\n");
1150         hlpfile->numFonts = 0;
1151         hlpfile->fonts = NULL;
1152         return FALSE;
1153     }
1154
1155     ref += 9;
1156
1157     face_num    = GET_USHORT(ref, 0);
1158     dscr_num    = GET_USHORT(ref, 2);
1159     face_offset = GET_USHORT(ref, 4);
1160     dscr_offset = GET_USHORT(ref, 6);
1161
1162     WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1163                face_num, face_offset, dscr_num, dscr_offset);
1164
1165     hlpfile->numFonts = dscr_num;
1166     hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1167
1168     len = (dscr_offset - face_offset) / face_num;
1169 /* EPP     for (i = face_offset; i < dscr_offset; i += len) */
1170 /* EPP         WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1171     for (i = 0; i < dscr_num; i++)
1172     {
1173         flag = ref[dscr_offset + i * 11 + 0];
1174         family = ref[dscr_offset + i * 11 + 2];
1175
1176         hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2;
1177         hlpfile->fonts[i].LogFont.lfWidth = 0;
1178         hlpfile->fonts[i].LogFont.lfEscapement = 0;
1179         hlpfile->fonts[i].LogFont.lfOrientation = 0;
1180         hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1181         hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1182         hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1183         hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1184         hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
1185         hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1186         hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1187         hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1188         hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1189
1190         switch (family)
1191         {
1192         case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN;     break;
1193         case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN;      break;
1194         case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS;      break;
1195         case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT;     break;
1196         case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1197         default: WINE_FIXME("Unknown family %u\n", family);
1198         }
1199         idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1200
1201         if (idx < face_num)
1202         {
1203             strncpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1204             hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1) + 1] = '\0';
1205         }
1206         else
1207         {
1208             WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1209             strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1210         }
1211         hlpfile->fonts[i].hFont = 0;
1212         hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1213                                       ref[dscr_offset + i * 11 + 6],
1214                                       ref[dscr_offset + i * 11 + 7]);
1215 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1216         WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1217                    i, flag,
1218                    X(0, "bold"),
1219                    X(1, "italic"),
1220                    X(2, "underline"),
1221                    X(3, "strikeOut"),
1222                    X(4, "dblUnderline"),
1223                    X(5, "smallCaps"),
1224                    ref[dscr_offset + i * 11 + 1],
1225                    family,
1226                    hlpfile->fonts[i].LogFont.lfFaceName, idx,
1227                    GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1228     }
1229     return TRUE;
1230 }
1231
1232 /***********************************************************************
1233  *
1234  *           HLPFILE_ReadFileToBuffer
1235  */
1236 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1237 {
1238     BYTE  header[16], dummy[1];
1239     UINT  size;
1240
1241     if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1242
1243     /* sanity checks */
1244     if (GET_UINT(header, 0) != 0x00035F3F)
1245     {WINE_WARN("wrong header\n"); return FALSE;};
1246
1247     size = GET_UINT(header, 12);
1248     file_buffer = HeapAlloc(GetProcessHeap(), 0, size + 1);
1249     if (!file_buffer) return FALSE;
1250
1251     memcpy(file_buffer, header, 16);
1252     if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
1253     {WINE_WARN("filesize1\n"); return FALSE;};
1254
1255     if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1256
1257     file_buffer[size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1258
1259     return TRUE;
1260 }
1261
1262 /***********************************************************************
1263  *
1264  *           HLPFILE_FindSubFile
1265  */
1266 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1267 {
1268     BYTE *root = file_buffer + GET_UINT(file_buffer,  4);
1269     BYTE *end  = file_buffer + GET_UINT(file_buffer, 12);
1270     BYTE *ptr;
1271     BYTE *bth;
1272
1273     unsigned    pgsize;
1274     unsigned    pglast;
1275     unsigned    nentries;
1276     unsigned    i, n;
1277
1278     bth = root + 9;
1279
1280     /* FIXME: this should be using the EnumBTree functions from this file */
1281     pgsize = GET_USHORT(bth, 4);
1282     WINE_TRACE("%s => pgsize=%u #pg=%u rootpg=%u #lvl=%u\n", 
1283                name, pgsize, GET_USHORT(bth, 30), GET_USHORT(bth, 26), GET_USHORT(bth, 32));
1284
1285     ptr = bth + 38 + GET_USHORT(bth, 26) * pgsize;
1286
1287     for (n = 1; n < GET_USHORT(bth, 32); n++)
1288     {
1289         nentries = GET_USHORT(ptr, 2);
1290         pglast = GET_USHORT(ptr, 4);
1291         WINE_TRACE("[%u]: #entries=%u next=%u\n", n, nentries, pglast);
1292
1293         ptr += 6;
1294         for (i = 0; i < nentries; i++)
1295         {
1296             WINE_TRACE("<= %s\n", ptr);
1297             if (strcmp(name, ptr) < 0) break;
1298             ptr += strlen(ptr) + 1;
1299             pglast = GET_USHORT(ptr, 0);
1300             ptr += 2;
1301         }
1302         ptr = bth + 38 + pglast * pgsize;
1303     }
1304
1305     nentries = GET_USHORT(ptr, 2);
1306     ptr += 8;
1307     for (i = 0; i < nentries; i++)
1308     {
1309         char*   fname = ptr;
1310         ptr += strlen(fname) + 1;
1311         WINE_TRACE("\\- %s\n", fname);
1312         if (strcmp(fname, name) == 0)
1313         {
1314             *subbuf = file_buffer + GET_UINT(ptr, 0);
1315             *subend = *subbuf + GET_UINT(*subbuf, 0);
1316             if (file_buffer > *subbuf || *subbuf > *subend || *subend > end)
1317             {
1318                 WINE_WARN("size mismatch\n");
1319                 return FALSE;
1320             }
1321             return TRUE;
1322         }
1323         ptr += 4;
1324     }
1325
1326     return FALSE;
1327 }
1328
1329 /***********************************************************************
1330  *
1331  *           HLPFILE_SystemCommands
1332  */
1333 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1334 {
1335     BYTE *buf, *ptr, *end;
1336     HLPFILE_MACRO *macro, **m;
1337     LPSTR p;
1338     unsigned short magic, minor, major, flags;
1339
1340     hlpfile->lpszTitle = NULL;
1341
1342     if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1343
1344     magic = GET_USHORT(buf + 9, 0);
1345     minor = GET_USHORT(buf + 9, 2);
1346     major = GET_USHORT(buf + 9, 4);
1347     /* gen date on 4 bytes */
1348     flags = GET_USHORT(buf + 9, 10);
1349     WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1350                magic, major, minor, flags);
1351     if (magic != 0x036C || major != 1)
1352     {WINE_WARN("Wrong system header\n"); return FALSE;}
1353     if (minor <= 16) {WINE_WARN("too old file format (NIY)\n"); return FALSE;}
1354     if (flags & 8) {WINE_WARN("Unsupported yet page size\n"); return FALSE;}
1355
1356     hlpfile->version = minor;
1357     hlpfile->flags = flags;
1358
1359     for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1360     {
1361         switch (GET_USHORT(ptr, 0))
1362         {
1363         case 1:
1364             if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1365             hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(ptr + 4) + 1);
1366             if (!hlpfile->lpszTitle) return FALSE;
1367             lstrcpy(hlpfile->lpszTitle, ptr + 4);
1368             WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1369             break;
1370
1371         case 2:
1372             if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1373             hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(ptr + 4) + 1);
1374             if (!hlpfile->lpszCopyright) return FALSE;
1375             lstrcpy(hlpfile->lpszCopyright, ptr + 4);
1376             WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1377             break;
1378
1379         case 3:
1380             if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1381             hlpfile->contents_start = GET_UINT(ptr, 4);
1382             WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1383             break;
1384
1385         case 4:
1386             macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1);
1387             if (!macro) break;
1388             p = (char*)macro + sizeof(HLPFILE_MACRO);
1389             lstrcpy(p, (LPSTR)ptr + 4);
1390             macro->lpszMacro = p;
1391             macro->next = 0;
1392             for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1393             *m = macro;
1394             break;
1395
1396         case 6:
1397             if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1398             hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows, 
1399                                            sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1400             if (hlpfile->windows)
1401             {
1402                 unsigned flags = GET_USHORT(ptr, 4);
1403                 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1404
1405                 if (flags & 0x0001) strcpy(wi->type, ptr + 6); else wi->type[0] = '\0';
1406                 if (flags & 0x0002) strcpy(wi->name, ptr + 16); else wi->name[0] = '\0';
1407                 if (flags & 0x0004) strcpy(wi->caption, ptr + 25); else strncpy(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1408                 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1409                 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1410                 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1411                 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1412                 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1413                 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1414                 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1415                 WINE_TRACE("System-Window: flags=%c%c%c%c%c%c%c%c type=%s name=%s caption=%s (%ld,%ld)x(%ld,%ld)\n",
1416                            flags & 0x0001 ? 'T' : 't',
1417                            flags & 0x0002 ? 'N' : 'n',
1418                            flags & 0x0004 ? 'C' : 'c',
1419                            flags & 0x0008 ? 'X' : 'x',
1420                            flags & 0x0010 ? 'Y' : 'y',
1421                            flags & 0x0020 ? 'W' : 'w',
1422                            flags & 0x0040 ? 'H' : 'h',
1423                            flags & 0x0080 ? 'S' : 's',
1424                            wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1425                            wi->size.cx, wi->size.cy);
1426             }
1427             break;
1428         default:
1429             WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1430         }
1431     }
1432     return TRUE;
1433 }
1434
1435 /***********************************************************************
1436  *
1437  *           HLPFILE_UncompressedLZ77_Size
1438  */
1439 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1440 {
1441     int  i, newsize = 0;
1442
1443     while (ptr < end)
1444     {
1445         int mask = *ptr++;
1446         for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1447         {
1448             if (mask & 1)
1449             {
1450                 int code = GET_USHORT(ptr, 0);
1451                 int len  = 3 + (code >> 12);
1452                 newsize += len;
1453                 ptr     += 2;
1454             }
1455             else newsize++, ptr++;
1456         }
1457     }
1458
1459     return newsize;
1460 }
1461
1462 /***********************************************************************
1463  *
1464  *           HLPFILE_UncompressLZ77
1465  */
1466 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1467 {
1468     int i;
1469
1470     while (ptr < end)
1471     {
1472         int mask = *ptr++;
1473         for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1474         {
1475             if (mask & 1)
1476             {
1477                 int code   = GET_USHORT(ptr, 0);
1478                 int len    = 3 + (code >> 12);
1479                 int offset = code & 0xfff;
1480                 memcpy(newptr, newptr - offset - 1, len);
1481                 newptr += len;
1482                 ptr    += 2;
1483             }
1484             else *newptr++ = *ptr++;
1485         }
1486     }
1487
1488     return newptr;
1489 }
1490
1491 /***********************************************************************
1492  *
1493  *           HLPFILE_UncompressLZ77_Phrases
1494  */
1495 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1496 {
1497     UINT i, num, dec_size;
1498     BYTE *buf, *end;
1499
1500     if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1501
1502     num = phrases.num = GET_USHORT(buf, 9);
1503     if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1504
1505     dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1506
1507     phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1508     phrases.buffer  = HeapAlloc(GetProcessHeap(), 0, dec_size);
1509     if (!phrases.offsets || !phrases.buffer) return FALSE;
1510
1511     for (i = 0; i <= num; i++)
1512         phrases.offsets[i] = GET_USHORT(buf, 0x11 + 2 * i) - 2 * num - 2;
1513
1514     HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, phrases.buffer);
1515
1516     hlpfile->hasPhrases = TRUE;
1517     return TRUE;
1518 }
1519
1520 /***********************************************************************
1521  *
1522  *           HLPFILE_Uncompress_Phrases40
1523  */
1524 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1525 {
1526     UINT num, dec_size, cpr_size;
1527     BYTE *buf_idx, *end_idx;
1528     BYTE *buf_phs, *end_phs;
1529     short i, n;
1530     long* ptr, mask = 0;
1531     unsigned short bc;
1532
1533     if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1534         !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1535
1536     ptr = (long*)(buf_idx + 9 + 28);
1537     bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1538     num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1539
1540     WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1541                "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1542                GET_UINT(buf_idx, 9 + 0),
1543                GET_UINT(buf_idx, 9 + 4),
1544                GET_UINT(buf_idx, 9 + 8),
1545                GET_UINT(buf_idx, 9 + 12),
1546                GET_UINT(buf_idx, 9 + 16),
1547                GET_UINT(buf_idx, 9 + 20),
1548                GET_USHORT(buf_idx, 9 + 24),
1549                GET_USHORT(buf_idx, 9 + 26));
1550
1551     dec_size = GET_UINT(buf_idx, 9 + 12);
1552     cpr_size = GET_UINT(buf_idx, 9 + 16);
1553
1554     if (dec_size != cpr_size &&
1555         dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1556     {
1557         WINE_WARN("size mismatch %u %u\n",
1558                   dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1559         dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1560     }
1561
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;
1565
1566 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1567
1568     phrases.offsets[0] = 0;
1569     for (i = 0; i < num; i++)
1570     {
1571         for (n = 1; getbit(); n += 1 << bc);
1572         if (getbit()) n++;
1573         if (bc > 1 && getbit()) n += 2;
1574         if (bc > 2 && getbit()) n += 4;
1575         if (bc > 3 && getbit()) n += 8;
1576         if (bc > 4 && getbit()) n += 16;
1577         phrases.offsets[i + 1] = phrases.offsets[i] + n;
1578     }
1579 #undef getbit
1580
1581     if (dec_size == cpr_size)
1582         memcpy(phrases.buffer, buf_phs + 9, dec_size);
1583     else
1584         HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, phrases.buffer);
1585
1586     hlpfile->hasPhrases = FALSE;
1587     return TRUE;
1588 }
1589
1590 /***********************************************************************
1591  *
1592  *           HLPFILE_Uncompress_Topic
1593  */
1594 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1595 {
1596     BYTE *buf, *ptr, *end, *newptr;
1597     int  i, newsize = 0;
1598
1599     if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1600     {WINE_WARN("topic0\n"); return FALSE;}
1601
1602     switch (hlpfile->flags & (8|4))
1603     {
1604     case 8:
1605         WINE_FIXME("Unsupported format\n");
1606         return FALSE;
1607     case 4:
1608         buf += 9;
1609         topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
1610         
1611         for (i = 0; i < topic.wMapLen; i++)
1612         {
1613             ptr = buf + i * 0x1000;
1614             
1615             /* I don't know why, it's necessary for printman.hlp */
1616             if (ptr + 0x44 > end) ptr = end - 0x44;
1617
1618             newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + 0x1000));
1619         }
1620         
1621         topic.map = HeapAlloc(GetProcessHeap(), 0,
1622                               topic.wMapLen * sizeof(topic.map[0]) + newsize);
1623         if (!topic.map) return FALSE;
1624         newptr = (char*)(topic.map + topic.wMapLen);
1625         topic.end = newptr + newsize;
1626
1627         for (i = 0; i < topic.wMapLen; i++)
1628         {
1629             ptr = buf + i * 0x1000;
1630             if (ptr + 0x44 > end) ptr = end - 0x44;
1631
1632             topic.map[i] = newptr;
1633             newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + 0x1000), newptr);
1634         }
1635         break;
1636     case 0:
1637         /* basically, we need to copy the 0x1000 byte pages (removing the first 0x0C) in
1638          * one single are in memory
1639          */
1640 #define DST_LEN (0x1000 - 0x0C)
1641         buf += 9;
1642         newsize = end - buf;
1643         /* number of destination pages */
1644         topic.wMapLen = (newsize - 1) / DST_LEN + 1;
1645         topic.map = HeapAlloc(GetProcessHeap(), 0,
1646                               topic.wMapLen * (sizeof(topic.map[0]) + DST_LEN));
1647         if (!topic.map) return FALSE;
1648         newptr = (char*)(topic.map + topic.wMapLen);
1649         topic.end = newptr + newsize;
1650
1651         for (i = 0; i < topic.wMapLen; i++)
1652         {
1653             topic.map[i] = newptr + i * DST_LEN;
1654             memcpy(topic.map[i], buf + i * 0x1000 + 0x0C, DST_LEN);
1655         }
1656 #undef DST_LEN
1657         break;
1658     }
1659     return TRUE;
1660 }
1661
1662 /***********************************************************************
1663  *
1664  *           HLPFILE_Uncompress2
1665  */
1666
1667 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1668 {
1669     BYTE *phptr, *phend;
1670     UINT code;
1671     UINT index;
1672
1673     while (ptr < end && newptr < newend)
1674     {
1675         if (!*ptr || *ptr >= 0x10)
1676             *newptr++ = *ptr++;
1677         else
1678         {
1679             code  = 0x100 * ptr[0] + ptr[1];
1680             index = (code - 0x100) / 2;
1681
1682             phptr = phrases.buffer + phrases.offsets[index];
1683             phend = phrases.buffer + phrases.offsets[index + 1];
1684
1685             if (newptr + (phend - phptr) > newend)
1686             {
1687                 WINE_FIXME("buffer overflow %p > %p for %d bytes\n", 
1688                            newptr, newend, phend - phptr);
1689                 return;
1690             }
1691             memcpy(newptr, phptr, phend - phptr);
1692             newptr += phend - phptr;
1693             if (code & 1) *newptr++ = ' ';
1694
1695             ptr += 2;
1696         }
1697     }
1698     if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1699 }
1700
1701 /******************************************************************
1702  *              HLPFILE_Uncompress3
1703  *
1704  *
1705  */
1706 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1707                                 const BYTE* src, const BYTE* src_end)
1708 {
1709     int         idx, len;
1710
1711     for (; src < src_end; src++)
1712     {
1713         if ((*src & 1) == 0)
1714         {
1715             idx = *src / 2;
1716             if (idx > phrases.num) 
1717             {
1718                 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1719                 len = 0;
1720             }
1721             else 
1722             {
1723                 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1724                 if (dst + len <= dst_end)
1725                     memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1726             }
1727         }
1728         else if ((*src & 0x03) == 0x01)
1729         {
1730             idx = (*src + 1) * 64;
1731             idx += *++src;
1732             if (idx > phrases.num) 
1733             {
1734                 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1735                 len = 0;
1736             }
1737             else
1738             {
1739                 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1740                 if (dst + len <= dst_end)
1741                     memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1742             }
1743         }
1744         else if ((*src & 0x07) == 0x03)
1745         {
1746             len = (*src / 8) + 1;
1747             if (dst + len <= dst_end)
1748                 memcpy(dst, src + 1, len);
1749             src += len;
1750         }
1751         else
1752         {
1753             len = (*src / 16) + 1;
1754             if (dst + len <= dst_end)
1755                 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1756         }
1757         dst += len;
1758     }
1759
1760     if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1761     return TRUE;
1762 }
1763
1764 /******************************************************************
1765  *              HLPFILE_UncompressRLE
1766  *
1767  *
1768  */
1769 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1770 {
1771     BYTE        ch;
1772     BYTE*       sdst = *dst + dstsz;
1773
1774     while (src < end)
1775     {
1776         ch = *src++;
1777         if (ch & 0x80)
1778         {
1779             ch &= 0x7F;
1780             if (ch == 0) WINE_FIXME("Null length 1, next is %u\n", *src);
1781             if ((*dst) + ch < sdst)
1782                 memcpy(*dst, src, ch);
1783             src += ch;
1784         }
1785         else
1786         {
1787             if ((*dst) + ch < sdst)
1788                 memset(*dst, (char)*src, ch);
1789             src++;
1790             if (ch == 0)
1791             {
1792                 WINE_FIXME("Null length 2, next is %u\n", *src);
1793             }
1794         }
1795         *dst += ch;
1796     }
1797     if (*dst != sdst)
1798         WINE_FIXME("Buffer X-flow: d(%u) instead of d(%u)\n",
1799                    *dst - (sdst - dstsz), dstsz);
1800 }
1801
1802 /******************************************************************
1803  *              HLPFILE_EnumBTreeLeaves
1804  *
1805  *
1806  */
1807 static void HLPFILE_EnumBTreeLeaves(const BYTE* buf, const BYTE* end, unsigned (*fn)(const BYTE*, void*), void* user)
1808 {
1809     unsigned    psize, pnext;
1810     unsigned    num, nlvl;
1811     const BYTE* ptr;
1812
1813     num    = GET_UINT(buf, 9 + 34);
1814     psize  = GET_USHORT(buf, 9 + 4);
1815     nlvl   = GET_USHORT(buf, 9 + 32);
1816     pnext  = GET_USHORT(buf, 9 + 26);
1817
1818     WINE_TRACE("BTree: #entries=%u pagSize=%u #levels=%u #pages=%u root=%u struct%16s\n",
1819                num, psize, nlvl, GET_USHORT(buf, 9 + 30), pnext, buf + 9 + 6);
1820     if (!num) return;
1821
1822     while (--nlvl > 0)
1823     {
1824         ptr = (buf + 9 + 38) + pnext * psize;
1825         WINE_TRACE("BTree: (index[%u]) unused=%u #entries=%u <%u\n",
1826                    pnext, GET_USHORT(ptr, 0), GET_USHORT(ptr, 2), GET_USHORT(ptr, 4));
1827         pnext = GET_USHORT(ptr, 4);
1828     }
1829     while (pnext != 0xFFFF)
1830     {
1831         const BYTE*     node_page;
1832         unsigned short  limit;
1833
1834         node_page = ptr = (buf + 9 + 38) + pnext * psize;
1835         limit = GET_USHORT(ptr, 2);
1836         WINE_TRACE("BTree: (leaf [%u]) unused=%u #entries=%u <%u >%u\n",
1837                    pnext, GET_USHORT(ptr, 0), limit, GET_USHORT(ptr, 4), GET_USHORT(ptr, 6));
1838         ptr += 8;
1839         while (limit--)
1840             ptr += (fn)(ptr, user);
1841         pnext = GET_USHORT(node_page, 6);
1842     }
1843 }
1844
1845 struct myfncb {
1846     HLPFILE*    hlpfile;
1847     int         i;
1848 };
1849
1850 static unsigned myfn(const BYTE* ptr, void* user)
1851 {
1852     struct myfncb*      m = user;
1853
1854     m->hlpfile->Context[m->i].lHash  = GET_UINT(ptr, 0);
1855     m->hlpfile->Context[m->i].offset = GET_UINT(ptr, 4);
1856     m->i++;
1857     return 8;
1858 }
1859
1860 /***********************************************************************
1861  *
1862  *           HLPFILE_GetContext
1863  */
1864 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
1865 {
1866     BYTE                *cbuf, *cend;
1867     struct myfncb       m;
1868     unsigned            clen;
1869
1870     if (!HLPFILE_FindSubFile("|CONTEXT",  &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
1871
1872     clen = GET_UINT(cbuf, 0x2b);
1873     hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen * sizeof(HLPFILE_CONTEXT));
1874     if (!hlpfile->Context) return FALSE;
1875     hlpfile->wContextLen = clen;
1876
1877     m.hlpfile = hlpfile;
1878     m.i = 0;
1879     HLPFILE_EnumBTreeLeaves(cbuf, cend, myfn, &m);
1880
1881     return TRUE;
1882 }
1883
1884 /******************************************************************
1885  *              HLPFILE_DeleteLink
1886  *
1887  *
1888  */
1889 void HLPFILE_FreeLink(HLPFILE_LINK* link)
1890 {
1891     if (link && !--link->wRefCount)
1892         HeapFree(GetProcessHeap(), 0, link);
1893 }
1894
1895 /***********************************************************************
1896  *
1897  *           HLPFILE_DeleteParagraph
1898  */
1899 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
1900 {
1901     HLPFILE_PARAGRAPH* next;
1902
1903     while (paragraph)
1904     {
1905         next = paragraph->next;
1906
1907         if (paragraph->cookie == para_metafile)
1908             DeleteMetaFile(paragraph->u.gfx.u.mf.hMetaFile);
1909
1910         HLPFILE_FreeLink(paragraph->link);
1911
1912         HeapFree(GetProcessHeap(), 0, paragraph);
1913         paragraph = next;
1914     }
1915 }
1916
1917 /***********************************************************************
1918  *
1919  *           DeleteMacro
1920  */
1921 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
1922 {
1923     HLPFILE_MACRO*      next;
1924
1925     while (macro)
1926     {
1927         next = macro->next;
1928         HeapFree(GetProcessHeap(), 0, macro);
1929         macro = next;
1930     }
1931 }
1932
1933 /***********************************************************************
1934  *
1935  *           DeletePage
1936  */
1937 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
1938 {
1939     HLPFILE_PAGE* next;
1940
1941     while (page)
1942     {
1943         next = page->next;
1944         HLPFILE_DeleteParagraph(page->first_paragraph);
1945         HLPFILE_DeleteMacro(page->first_macro);
1946         HeapFree(GetProcessHeap(), 0, page);
1947         page = next;
1948     }
1949 }
1950
1951 /***********************************************************************
1952  *
1953  *           HLPFILE_FreeHlpFile
1954  */
1955 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
1956 {
1957     unsigned i;
1958
1959     if (!hlpfile || --hlpfile->wRefCount > 0) return;
1960
1961     if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
1962     if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
1963     else first_hlpfile = hlpfile->next;
1964
1965     if (hlpfile->numFonts)
1966     {
1967         for (i = 0; i < hlpfile->numFonts; i++)
1968         {
1969             DeleteObject(hlpfile->fonts[i].hFont);
1970         }
1971         HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
1972     }
1973
1974     if (hlpfile->numBmps)
1975     {
1976         for (i = 0; i < hlpfile->numBmps; i++)
1977         {
1978             DeleteObject(hlpfile->bmps[i]);
1979         }
1980         HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
1981     }
1982
1983     HLPFILE_DeletePage(hlpfile->first_page);
1984     HLPFILE_DeleteMacro(hlpfile->first_macro);
1985
1986     if (hlpfile->numWindows)    HeapFree(GetProcessHeap(), 0, hlpfile->windows);
1987     if (hlpfile->Context)       HeapFree(GetProcessHeap(), 0, hlpfile->Context);
1988     if (hlpfile->lpszTitle)     HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
1989     if (hlpfile->lpszCopyright) HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
1990     HeapFree(GetProcessHeap(), 0, hlpfile);
1991 }