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