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