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