Fixed some compiler errors and warnings.
[wine] / programs / winhelp / hlpfile.c
1 /*
2  * Help Viewer
3  *
4  * Copyright 1996 Ulrich Schmid
5  */
6
7 #include <stdio.h>
8 #include <string.h>
9 #include "windows.h"
10 #include "windowsx.h"
11 #include "winhelp.h"
12
13 static void Report(LPCSTR str)
14 {
15 #if 0
16   fprintf(stderr, "%s\n", str);
17 #endif
18 }
19
20 #define GET_USHORT(buffer, i)\
21 (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1])))
22 #define GET_SHORT(buffer, i)\
23 (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1])))
24 #define GET_UINT(buffer, i)\
25 GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i+2)
26
27 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
28 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
29 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
30 static VOID HLPFILE_SystemCommands(HLPFILE*);
31 static BOOL HLPFILE_Uncompress1_Phrases();
32 static BOOL HLPFILE_Uncompress1_Topic();
33 static BOOL HLPFILE_GetContext(HLPFILE*);
34 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*);
35 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*);
36 static UINT HLPFILE_Uncompressed2_Size(BYTE*, BYTE*);
37 static VOID HLPFILE_Uncompress2(BYTE**, BYTE*, BYTE*);
38
39 static HLPFILE *first_hlpfile = 0;
40 static HGLOBAL  hFileBuffer;
41 static BYTE    *file_buffer;
42
43 static struct
44 {
45   UINT    num;
46   BYTE   *buf;
47   HGLOBAL hBuffer;
48 } phrases;
49
50 static struct
51 {
52   BYTE    **map;
53   BYTE    *end;
54   UINT    wMapLen;
55   HGLOBAL hMap;
56   HGLOBAL hBuffer;
57 } topic;
58
59 static struct
60 {
61   UINT  bDebug;
62   UINT  wFont;
63   UINT  wIndent;
64   UINT  wHSpace;
65   UINT  wVSpace;
66   UINT  wVBackSpace;
67   HLPFILE_LINK link;
68 } attributes;
69
70 /***********************************************************************
71  *
72  *           HLPFILE_Contents
73  */
74
75 HLPFILE_PAGE *HLPFILE_Contents(LPCSTR lpszPath)
76 {
77   HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
78
79   if (!hlpfile) return(0);
80
81   return(hlpfile->first_page);
82 }
83
84 /***********************************************************************
85  *
86  *           HLPFILE_PageByNumber
87  */
88
89 HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
90 {
91   HLPFILE_PAGE *page;
92   HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
93
94   if (!hlpfile) return(0);
95
96   for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
97
98   return page;
99 }
100
101 /***********************************************************************
102  *
103  *           HLPFILE_HlpFilePageByHash
104  */
105
106 HLPFILE_PAGE *HLPFILE_PageByHash(LPCSTR lpszPath, LONG lHash)
107 {
108   INT i;
109   UINT wNum;
110   HLPFILE_PAGE *page;
111   HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
112
113   if (!hlpfile) return(0);
114
115   for (i = 0; i < hlpfile->wContextLen; i++)
116     if (hlpfile->Context[i].lHash == lHash) break;
117
118   if (i >= hlpfile->wContextLen)
119     {
120       HLPFILE_FreeHlpFile(hlpfile);
121       return(0);
122     }
123
124   wNum = hlpfile->Context[i].wPage;
125   for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
126
127   return page;
128 }
129
130 /***********************************************************************
131  *
132  *           HLPFILE_Hash
133  */
134
135 LONG HLPFILE_Hash(LPCSTR lpszContext)
136 {
137   LONG lHash = 0;
138   CHAR c;
139   while((c = *lpszContext++))
140     {
141       CHAR x = 0;
142       if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
143       if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
144       if (c >= '1' && c <= '9') x = c - '0';
145       if (c == '0') x = 10;
146       if (c == '.') x = 12;
147       if (c == '_') x = 13;
148       if (x) lHash = lHash * 43 + x;
149     }
150   return lHash;
151 }
152
153 /***********************************************************************
154  *
155  *           HLPFILE_ReadHlpFile
156  */
157
158 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
159 {
160   HGLOBAL   hHlpFile;
161   HLPFILE *hlpfile;
162
163   for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
164     if (!lstrcmp(hlpfile->lpszPath, lpszPath))
165       {
166         hlpfile->wRefCount++;
167         return(hlpfile);
168       }
169
170   hHlpFile = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
171   if (!hHlpFile) return(0);
172
173   hlpfile              = GlobalLock(hHlpFile);
174   hlpfile->hSelf       = hHlpFile;
175   hlpfile->wRefCount   = 1;
176   hlpfile->hTitle      = 0;
177   hlpfile->hContext    = 0;
178   hlpfile->wContextLen = 0;
179   hlpfile->first_page  = 0;
180   hlpfile->first_macro = 0;
181   hlpfile->prev        = 0;
182   hlpfile->next        = first_hlpfile;
183   first_hlpfile   = hlpfile;
184   if (hlpfile->next) hlpfile->next->prev = hlpfile;
185
186   hlpfile->lpszPath   = GlobalLock(hHlpFile);
187   hlpfile->lpszPath  += sizeof(HLPFILE);
188   strcpy(hlpfile->lpszPath, lpszPath);
189
190   phrases.hBuffer = topic.hBuffer = hFileBuffer = 0;
191
192   if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
193     {
194       HLPFILE_FreeHlpFile(hlpfile);
195       hlpfile = 0;
196     }
197
198   if (phrases.hBuffer) GlobalFree(phrases.hBuffer);
199   if (topic.hBuffer)   GlobalFree(topic.hBuffer);
200   if (topic.hMap)      GlobalFree(topic.hMap);
201   if (hFileBuffer)     GlobalFree(hFileBuffer);
202
203   return(hlpfile);
204 }
205
206 /***********************************************************************
207  *
208  *           HLPFILE_DoReadHlpFile
209  */
210
211 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
212 {
213   BOOL     ret;
214   HFILE    hFile;
215   OFSTRUCT ofs;
216   BYTE     *buf;
217
218   hFile=OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH);
219   if (hFile == HFILE_ERROR) return FALSE;
220
221   ret = HLPFILE_ReadFileToBuffer(hFile);
222   _lclose(hFile);
223   if (!ret) return FALSE;
224
225   HLPFILE_SystemCommands(hlpfile);
226   if (!HLPFILE_Uncompress1_Phrases()) return FALSE;
227   if (!HLPFILE_Uncompress1_Topic()) return FALSE;
228
229   buf = topic.map[0] + 0xc;
230   while(buf + 0xc < topic.end)
231     {
232       BYTE *end = MIN(buf + GET_UINT(buf, 0), topic.end);
233       UINT next, index, offset;
234
235       switch (buf[0x14])
236         {
237         case 0x02:
238           if (!HLPFILE_AddPage(hlpfile, buf, end)) return(FALSE);
239           break;
240
241         case 0x20:
242           if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE);
243           break;
244
245         case 0x23:
246           if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE);
247           break;
248
249         default:
250           fprintf(stderr, "buf[0x14] = %x\n", buf[0x14]);
251         }
252
253       next   = GET_UINT(buf, 0xc);
254       if (next == 0xffffffff) break;
255
256       index  = next >> 14;
257       offset = next & 0x3fff;
258       if (index > topic.wMapLen) {Report("maplen"); break;}
259       buf = topic.map[index] + offset;
260     }
261
262   return(HLPFILE_GetContext(hlpfile));
263 }
264
265 /***********************************************************************
266  *
267  *           HLPFILE_AddPage
268  */
269
270 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end)
271 {
272   HGLOBAL   hPage;
273   HLPFILE_PAGE *page, **pageptr;
274   BYTE      *title;
275   UINT      titlesize;
276
277   for (pageptr = &hlpfile->first_page; *pageptr; pageptr = &(*pageptr)->next)
278     /* Nothing */; 
279
280   if (buf + 0x31 > end) {Report("page1"); return(FALSE);};
281   title = buf + GET_UINT(buf, 0x10);
282   if (title > end) {Report("page2"); return(FALSE);};
283
284   titlesize = HLPFILE_Uncompressed2_Size(title, end);
285   hPage = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PAGE) + titlesize);
286   if (!hPage) return FALSE;
287   page = *pageptr = GlobalLock(hPage);
288   pageptr               = &page->next;
289   page->hSelf           = hPage;
290   page->file            = hlpfile;
291   page->next            = 0;
292   page->first_paragraph = 0;
293
294   page->lpszTitle = GlobalLock(hPage);
295   page->lpszTitle += sizeof(HLPFILE_PAGE);
296   HLPFILE_Uncompress2(&title, end, page->lpszTitle);
297
298   page->wNumber = GET_UINT(buf, 0x21);
299
300   attributes.bDebug        = 0;
301   attributes.wFont         = 0;
302   attributes.wVSpace       = 0;
303   attributes.wVBackSpace   = 0;
304   attributes.wHSpace       = 0;
305   attributes.wIndent       = 0;
306   attributes.link.lpszPath = 0;
307
308   return TRUE;
309 }
310
311 /***********************************************************************
312  *
313  *           HLPFILE_AddParagraph
314  */
315
316 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end)
317 {
318   HGLOBAL            hParagraph;
319   HLPFILE_PAGE      *page;
320   HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
321   UINT               textsize;
322   BYTE              *format, *text;
323   BOOL               format_header = TRUE;
324   BOOL               format_end = FALSE;
325   UINT               mask, i;
326
327   if (!hlpfile->first_page) {Report("paragraph1"); return(FALSE);};
328
329   for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
330   for (paragraphptr = &page->first_paragraph; *paragraphptr;
331        paragraphptr = &(*paragraphptr)->next) /* Nothing */; 
332
333   if (buf + 0x19 > end) {Report("paragraph2"); return(FALSE);};
334
335   if (buf[0x14] == 0x02) return TRUE;
336
337   text   = buf + GET_UINT(buf, 0x10);
338
339   switch (buf[0x14])
340     {
341     case 0x20:
342       format = buf + 0x18;
343       while (*format) format++;
344       format += 4;
345       break;
346
347     case 0x23:
348       format = buf + 0x2b;
349       if (buf[0x17] & 1) format++;
350       break;
351
352     default:
353       Report("paragraph3");
354       return FALSE;
355     }
356
357   while (text < end)
358     {
359       if (format_header)
360         {
361           format_header = FALSE;
362
363           mask = GET_USHORT(format, 0);
364           mask &= 0x3ff;
365           format += 2;
366
367           for (i = 0; i < 10; i++, mask = mask >> 1)
368             {
369               if (mask & 1)
370                 {
371                   BOOL twoargs = FALSE;
372                   CHAR prefix0 = ' ';
373                   CHAR prefix1 = '*';
374
375                   if (i == 9 && !twoargs)
376                     {
377                       switch (*format++)
378                         {
379                         default:
380                           prefix0 = prefix1 = '?';
381                           break;
382
383                         case 0x82:
384                           prefix0 = prefix1 = 'x';
385                           break;
386
387                         case 0x84:
388                           prefix0 = prefix1 = 'X';
389                           twoargs = TRUE;
390                         }
391                     }
392
393                   if (*format & 1)
394                     switch(*format)
395                       {
396                       default:
397                         format += 2;
398                         break;
399                       }
400                   else
401                     switch(*format)
402                       {
403
404                       default:
405                         format++;
406                         break;
407
408                       case 0x08:
409                         format += 3;
410                         break;
411                       }
412
413                   if (twoargs) format += (*format & 1) ? 2 : 1;
414                 }
415             }
416         }
417
418       for (; !format_header && text < end && format < end && !*text; text++)
419         {
420           switch(*format)
421             {
422             case 0x80:
423               attributes.wFont = GET_USHORT(format, 1);
424               format += 3;
425               break;
426
427             case 0x81:
428               attributes.wVSpace++;
429               format += 1;
430               break;
431
432             case 0x82:
433               attributes.wVSpace += 2 - attributes.wVBackSpace;
434               attributes.wVBackSpace = 0;
435               attributes.wIndent = 0;
436               format += 1;
437               break;
438
439             case 0x83:
440               attributes.wIndent++;
441               format += 1;
442               break;
443
444             case 0x84:
445               format += 3;
446               break;
447
448             case 0x86:
449             case 0x87:
450             case 0x88:
451               format += 9;
452               break;
453
454             case 0x89:
455               attributes.wVBackSpace++;
456               format += 1;
457               break;
458
459             case 0xa9:
460               format += 2;
461               break;
462
463             case 0xe2:
464             case 0xe3:
465               attributes.link.lpszPath = hlpfile->lpszPath;
466               attributes.link.lHash    = GET_UINT(format, 1);
467               attributes.link.bPopup   = !(*format & 1);
468               format += 5;
469               break;
470
471             case 0xea:
472               attributes.link.lpszPath = format + 8;
473               attributes.link.lHash    = GET_UINT(format, 4);
474               attributes.link.bPopup   = !(*format & 1);
475               format += 3 + GET_USHORT(format, 1);
476               break;
477
478             case 0xff:
479               if (buf[0x14] != 0x23 || GET_USHORT(format, 1) == 0xffff)
480                 {
481                   if (format_end) Report("format_end");
482                   format_end = TRUE;
483                   break;
484                 }
485               else
486                 {
487                   format_header = TRUE;
488                   format += 10;
489                   break;
490                 }
491
492             default:
493               Report("format");
494               format++;
495             }
496         }
497
498       if (text > end || format > end) {Report("paragraph_end"); return(FALSE);};
499       if (text == end && !format_end) Report("text_end");
500
501       if (text == end) break;
502
503       textsize = HLPFILE_Uncompressed2_Size(text, end);
504       hParagraph = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PARAGRAPH) + textsize);
505       if (!hParagraph) return FALSE;
506       paragraph = *paragraphptr = GlobalLock(hParagraph);
507       paragraphptr = &paragraph->next;
508       paragraph->hSelf    = hParagraph;
509       paragraph->next     = 0;
510       paragraph->link     = 0;
511
512       paragraph->lpszText = GlobalLock(hParagraph);
513       paragraph->lpszText += sizeof(HLPFILE_PARAGRAPH);
514       HLPFILE_Uncompress2(&text, end, paragraph->lpszText);
515
516       paragraph->bDebug      = attributes.bDebug;
517       paragraph->wFont       = attributes.wFont;
518       paragraph->wVSpace     = attributes.wVSpace;
519       paragraph->wHSpace     = attributes.wHSpace;
520       paragraph->wIndent     = attributes.wIndent;
521       if (attributes.link.lpszPath)
522         {
523           LPSTR ptr;
524           HGLOBAL handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_LINK) +
525                                            strlen(attributes.link.lpszPath) + 1);
526           if (!handle) return FALSE;
527           paragraph->link = GlobalLock(handle);
528           paragraph->link->hSelf = handle;
529
530           ptr = GlobalLock(handle);
531           ptr += sizeof(HLPFILE_LINK);
532           lstrcpy(ptr, (LPSTR) attributes.link.lpszPath);
533
534           paragraph->link->lpszPath = ptr;
535           paragraph->link->lHash    = attributes.link.lHash;
536           paragraph->link->bPopup   = attributes.link.bPopup;
537         }
538
539       attributes.bDebug        = 0;
540       attributes.wVSpace       = 0;
541       attributes.wHSpace       = 0;
542       attributes.link.lpszPath = 0;
543     }
544
545   return TRUE;
546 }
547
548 /***********************************************************************
549  *
550  *           HLPFILE_ReadFileToBuffer
551  */
552
553 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
554 {
555   BYTE  header[16], dummy[1];
556   UINT  size;
557
558   if (_hread(hFile, header, 16) != 16) {Report("header"); return(FALSE);};
559
560   size = GET_UINT(header, 12);
561   hFileBuffer = GlobalAlloc(GMEM_FIXED, size + 1);
562   if (!hFileBuffer) return FALSE;
563   file_buffer = GlobalLock(hFileBuffer);
564
565   memcpy(file_buffer, header, 16);
566   if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
567     {Report("filesize1"); return(FALSE);};
568
569   if (_hread(hFile, dummy, 1) != 0) Report("filesize2"); 
570
571   file_buffer[size] = '0';
572
573   return TRUE;
574 }
575
576 /***********************************************************************
577  *
578  *           HLPFILE_FindSubFile
579  */
580
581 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
582 {
583   BYTE *root = file_buffer + GET_UINT(file_buffer,  4);
584   BYTE *end  = file_buffer + GET_UINT(file_buffer, 12);
585   BYTE *ptr  = root + 0x37;
586
587   while (ptr < end && ptr[0] == 0x7c)
588     {
589       BYTE *fname = ptr + 1;
590       ptr += strlen(ptr) + 1;
591       if (!lstrcmpi(fname, name))
592         {
593           *subbuf = file_buffer + GET_UINT(ptr, 0);
594           *subend = *subbuf + GET_UINT(*subbuf, 0);
595           if (file_buffer > *subbuf || *subbuf > *subend || *subend >= end)
596             {
597               Report("subfile");
598               return FALSE;
599             }
600           return TRUE;
601         }
602       else ptr += 4;
603     }
604   return FALSE;
605 }
606
607 /***********************************************************************
608  *
609  *           HLPFILE_SystemCommands
610  */
611 static VOID HLPFILE_SystemCommands(HLPFILE* hlpfile)
612 {
613   BYTE *buf, *ptr, *end;
614   HGLOBAL handle;
615   HLPFILE_MACRO *macro, **m;
616   LPSTR p;
617
618   hlpfile->lpszTitle = "";
619
620   if (!HLPFILE_FindSubFile("SYSTEM", &buf, &end)) return;
621
622   for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
623     {
624       switch (GET_USHORT(ptr, 0))
625         {
626         case 1:
627           if (hlpfile->hTitle) {Report("title"); break;}
628           hlpfile->hTitle = GlobalAlloc(GMEM_FIXED, strlen(ptr + 4) + 1);
629           if (!hlpfile->hTitle) return;
630           hlpfile->lpszTitle = GlobalLock(hlpfile->hTitle);
631           lstrcpy(hlpfile->lpszTitle, ptr + 4);
632           break;
633
634         case 2:
635           if (GET_USHORT(ptr, 2) != 1 || ptr[4] != 0) Report("system2");
636           break;
637
638         case 3:
639           if (GET_USHORT(ptr, 2) != 4 || GET_UINT(ptr, 4) != 0) Report("system3");
640           break;
641
642         case 4:
643           handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1);
644           if (!handle) break;
645           macro = GlobalLock(handle);
646           macro->hSelf = handle;
647           p  = GlobalLock(handle);
648           p += sizeof(HLPFILE_MACRO);
649           lstrcpy(p, (LPSTR) ptr + 4);
650           macro->lpszMacro = p;
651           macro->next = 0;
652           for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
653           *m = macro;
654           break;
655
656         default:
657           Report("system");
658         }
659     }
660 }
661
662 /***********************************************************************
663  *
664  *           HLPFILE_Uncompressed1_Size
665  */
666
667 static INT HLPFILE_Uncompressed1_Size(BYTE *ptr, BYTE *end)
668 {
669   INT  i, newsize = 0;
670
671   while (ptr < end)
672     {
673       INT mask=*ptr++;
674       for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
675         {
676           if (mask & 1)
677             {
678               INT code = GET_USHORT(ptr, 0);
679               INT len  = 3 + (code >> 12);
680               newsize += len;
681               ptr     += 2;
682             }
683           else newsize++, ptr++;
684         }
685     }
686
687   return(newsize);
688 }
689
690 /***********************************************************************
691  *
692  *           HLPFILE_Uncompress1
693  */
694
695 static BYTE *HLPFILE_Uncompress1(BYTE *ptr, BYTE *end, BYTE *newptr)
696 {
697   INT i;
698
699   while (ptr < end)
700     {
701       INT mask=*ptr++;
702       for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
703         {
704           if (mask & 1)
705             {
706               INT code   = GET_USHORT(ptr, 0);
707               INT len    = 3 + (code >> 12);
708               INT offset = code & 0xfff;
709               hmemcpy16(newptr, newptr - offset - 1, len);
710               newptr += len;
711               ptr    += 2;
712             }
713           else *newptr++ = *ptr++;
714         }
715     }
716
717   return(newptr);
718 }
719
720 /***********************************************************************
721  *
722  *           HLPFILE_Uncompress1_Phrases
723  */
724
725 static BOOL HLPFILE_Uncompress1_Phrases()
726 {
727   UINT i, num, newsize;
728   BYTE *buf, *end, *newbuf;
729
730   if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) {Report("phrases0"); return FALSE;}
731
732   num = phrases.num = GET_USHORT(buf, 9);
733   if (buf + 2 * num + 0x13 >= end) {Report("uncompress1a"); return(FALSE);};
734
735   newsize  = 2 * num + 2;
736   newsize += HLPFILE_Uncompressed1_Size(buf + 0x13 + 2 * num, end);
737   phrases.hBuffer = GlobalAlloc(GMEM_FIXED, newsize);
738   if (!phrases.hBuffer) return FALSE;
739   newbuf = phrases.buf = GlobalLock(phrases.hBuffer);
740
741   hmemcpy16(newbuf, buf + 0x11, 2 * num + 2);
742   HLPFILE_Uncompress1(buf + 0x13 + 2 * num, end, newbuf + 2 * num + 2);
743
744   for (i = 0; i < num; i++)
745     {
746       INT i0 = GET_USHORT(newbuf, 2 * i);
747       INT i1 = GET_USHORT(newbuf, 2 * i + 2);
748       if (i1 < i0 || i1 > newsize) {Report("uncompress1b"); return(FALSE);};
749     }
750   return TRUE;
751 }
752
753 /***********************************************************************
754  *
755  *           HLPFILE_Uncompress1_Topic
756  */
757
758 static BOOL HLPFILE_Uncompress1_Topic()
759 {
760   BYTE *buf, *ptr, *end, *newptr;
761   INT  i, newsize = 0;
762
763   if (!HLPFILE_FindSubFile("TOPIC", &buf, &end)) {Report("topic0"); return FALSE;}
764
765   buf += 9;
766   topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
767
768   for (i = 0; i < topic.wMapLen; i++)
769     {
770       ptr = buf + i * 0x1000;
771
772       /* I don't know why, it's necessary for printman.hlp */
773       if (ptr + 0x44 > end) ptr = end - 0x44;
774
775       newsize += HLPFILE_Uncompressed1_Size(ptr + 0xc, MIN(end, ptr + 0x1000));
776     }
777
778   topic.hMap    = GlobalAlloc(GMEM_FIXED, topic.wMapLen * sizeof(topic.map[0]));
779   topic.hBuffer = GlobalAlloc(GMEM_FIXED, newsize);
780   if (!topic.hMap || !topic.hBuffer) return FALSE;
781   topic.map = GlobalLock(topic.hMap);
782   newptr = GlobalLock(topic.hBuffer);
783   topic.end = newptr + newsize;
784
785   for (i = 0; i < topic.wMapLen; i++)
786     {
787       ptr = buf + i * 0x1000;
788       if (ptr + 0x44 > end) ptr = end - 0x44;
789
790       topic.map[i] = newptr - 0xc;
791       newptr = HLPFILE_Uncompress1(ptr + 0xc, MIN(end, ptr + 0x1000), newptr);
792     }
793
794   return TRUE;
795 }
796
797 /***********************************************************************
798  *
799  *           HLPFILE_Uncompressed2_Size
800  */
801
802 static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end)
803 {
804   UINT wSize   = 0;
805
806   while (ptr < end && *ptr)
807     {
808       if (*ptr >= 0x20)
809         wSize++, ptr++;
810       else
811         {
812           BYTE *phptr, *phend;
813           UINT code  = 0x100 * ptr[0] + ptr[1];
814           UINT index = (code - 0x100) / 2;
815           BOOL space = code & 1;
816
817           if (index < phrases.num)
818             {
819               phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
820               phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
821
822               if (phend < phptr) Report("uncompress2a");
823
824               wSize += phend - phptr;
825               if (space) wSize++;
826             }
827           else Report("uncompress2b");
828
829           ptr += 2;
830         }
831     }
832
833   return(wSize + 1);
834 }
835
836 /***********************************************************************
837  *
838  *           HLPFILE_Uncompress2
839  */
840
841 static VOID HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr)
842 {
843   BYTE *ptr    = *pptr;
844
845   while (ptr < end && *ptr)
846     {
847       if (*ptr >= 0x20)
848         *newptr++ = *ptr++;
849       else
850         {
851           BYTE *phptr, *phend;
852           UINT code  = 0x100 * ptr[0] + ptr[1];
853           UINT index = (code - 0x100) / 2;
854           BOOL space = code & 1;
855
856           phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
857           phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
858
859           hmemcpy16(newptr, phptr, phend - phptr);
860           newptr += phend - phptr;
861           if (space) *newptr++ = ' ';
862
863           ptr += 2;
864         }
865     }
866   *newptr  = '\0';
867   *pptr    = ptr;
868 }
869
870 /***********************************************************************
871  *
872  *           HLPFILE_GetContext
873  */
874
875 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
876 {
877   UINT i, j, clen, tlen;
878   BYTE *cbuf, *cptr, *cend, *tbuf, *tptr, *tend;
879
880   if (!HLPFILE_FindSubFile("CONTEXT", &cbuf, &cend)) {Report("context0"); return FALSE;}
881   if (cbuf + 0x37 > cend) {Report("context1"); return(FALSE);};
882   clen = GET_UINT(cbuf, 0x2b);
883   if (cbuf + 0x37 + 8 * hlpfile->wContextLen > cend) {Report("context2"); return(FALSE);};
884
885   if (!HLPFILE_FindSubFile("TTLBTREE", &tbuf, &tend)) {Report("ttlb0"); return FALSE;}
886   if (tbuf + 0x37 > tend) {Report("ttlb1"); return(FALSE);};
887   tlen = GET_UINT(tbuf, 0x2b);
888
889   hlpfile->hContext = GlobalAlloc(GMEM_FIXED, clen * sizeof(HLPFILE_CONTEXT));
890   if (!hlpfile->hContext) return FALSE;
891   hlpfile->Context = GlobalLock(hlpfile->hContext);
892   hlpfile->wContextLen = clen;
893
894   cptr = cbuf + 0x37;
895   for (i = 0; i < clen; i++, cptr += 8)
896     {
897       tptr = tbuf + 0x37;
898       for (j = 0; j < tlen; j++, tptr += 5 + strlen(tptr + 4))
899         {
900           if (tptr + 4 >= tend) {Report("ttlb2"); return(FALSE);};
901           if (GET_UINT(tptr, 0) == GET_UINT(cptr, 4)) break;
902         }
903       if (j >= tlen)
904         {
905           Report("ttlb3");
906           j = 0;
907         }
908       hlpfile->Context[i].lHash = GET_UINT(cptr, 0);
909       hlpfile->Context[i].wPage = j;
910     }
911
912   return TRUE;
913 }
914
915 /***********************************************************************
916  *
917  *           HLPFILE_DeleteParagraph
918  */
919
920 static VOID HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
921 {
922   if (!paragraph) return;
923
924   if (paragraph->link) GlobalFree(paragraph->link->hSelf);
925
926   HLPFILE_DeleteParagraph(paragraph->next);
927   GlobalFree(paragraph->hSelf);
928 }
929
930 /***********************************************************************
931  *
932  *           DeletePage
933  */
934
935 static VOID HLPFILE_DeletePage(HLPFILE_PAGE* page)
936 {
937   if (!page) return;
938
939   HLPFILE_DeletePage(page->next);
940   HLPFILE_DeleteParagraph(page->first_paragraph);
941   GlobalFree(page->hSelf);
942 }
943
944 /***********************************************************************
945  *
946  *           DeleteMacro
947  */
948
949 static VOID HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
950 {
951   if (!macro) return;
952
953   HLPFILE_DeleteMacro(macro->next);
954   GlobalFree(macro->hSelf);
955 }
956
957 /***********************************************************************
958  *
959  *           HLPFILE_FreeHlpFile
960  */
961
962 VOID HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
963 {
964   if (!hlpfile) return;
965   if (--hlpfile->wRefCount) return;
966
967   if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
968   if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
969   else first_hlpfile = 0;
970
971   HLPFILE_DeletePage(hlpfile->first_page);
972   HLPFILE_DeleteMacro(hlpfile->first_macro);
973   if (hlpfile->hContext) GlobalFree(hlpfile->hContext);
974   if (hlpfile->hTitle) GlobalFree(hlpfile->hTitle);
975   GlobalFree(hlpfile->hSelf);
976 }
977
978 /***********************************************************************
979  *
980  *           FreeHlpFilePage
981  */
982
983 VOID HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page)
984 {
985   if (!page) return;
986   HLPFILE_FreeHlpFile(page->file);
987 }
988
989 /* Local Variables:    */
990 /* c-file-style: "GNU" */
991 /* End:                */