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