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