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