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