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