Added mappings for a few messages.
[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           m = &hlpfile->first_macro;
653           while (*m) m = &(*m)->next;
654           *m = macro;
655           break;
656
657         default:
658           Report("system");
659         }
660     }
661 }
662
663 /***********************************************************************
664  *
665  *           HLPFILE_Uncompressed1_Size
666  */
667
668 static INT HLPFILE_Uncompressed1_Size(BYTE *ptr, BYTE *end)
669 {
670   INT  i, newsize = 0;
671
672   while (ptr < end)
673     {
674       INT mask=*ptr++;
675       for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
676         {
677           if (mask & 1)
678             {
679               INT code = GET_USHORT(ptr, 0);
680               INT len  = 3 + (code >> 12);
681               newsize += len;
682               ptr     += 2;
683             }
684           else newsize++, ptr++;
685         }
686     }
687
688   return(newsize);
689 }
690
691 /***********************************************************************
692  *
693  *           HLPFILE_Uncompress1
694  */
695
696 static BYTE *HLPFILE_Uncompress1(BYTE *ptr, BYTE *end, BYTE *newptr)
697 {
698   INT i;
699
700   while (ptr < end)
701     {
702       INT mask=*ptr++;
703       for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
704         {
705           if (mask & 1)
706             {
707               INT code   = GET_USHORT(ptr, 0);
708               INT len    = 3 + (code >> 12);
709               INT offset = code & 0xfff;
710               memcpy(newptr, newptr - offset - 1, len);
711               newptr += len;
712               ptr    += 2;
713             }
714           else *newptr++ = *ptr++;
715         }
716     }
717
718   return(newptr);
719 }
720
721 /***********************************************************************
722  *
723  *           HLPFILE_Uncompress1_Phrases
724  */
725
726 static BOOL HLPFILE_Uncompress1_Phrases()
727 {
728   UINT i, num, newsize;
729   BYTE *buf, *end, *newbuf;
730
731   if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) {Report("phrases0"); return FALSE;}
732
733   num = phrases.num = GET_USHORT(buf, 9);
734   if (buf + 2 * num + 0x13 >= end) {Report("uncompress1a"); return(FALSE);};
735
736   newsize  = 2 * num + 2;
737   newsize += HLPFILE_Uncompressed1_Size(buf + 0x13 + 2 * num, end);
738   phrases.hBuffer = GlobalAlloc(GMEM_FIXED, newsize);
739   if (!phrases.hBuffer) return FALSE;
740   newbuf = phrases.buf = GlobalLock(phrases.hBuffer);
741
742   memcpy(newbuf, buf + 0x11, 2 * num + 2);
743   HLPFILE_Uncompress1(buf + 0x13 + 2 * num, end, newbuf + 2 * num + 2);
744
745   for (i = 0; i < num; i++)
746     {
747       INT i0 = GET_USHORT(newbuf, 2 * i);
748       INT i1 = GET_USHORT(newbuf, 2 * i + 2);
749       if (i1 < i0 || i1 > newsize) {Report("uncompress1b"); return(FALSE);};
750     }
751   return TRUE;
752 }
753
754 /***********************************************************************
755  *
756  *           HLPFILE_Uncompress1_Topic
757  */
758
759 static BOOL HLPFILE_Uncompress1_Topic()
760 {
761   BYTE *buf, *ptr, *end, *newptr;
762   INT  i, newsize = 0;
763
764   if (!HLPFILE_FindSubFile("TOPIC", &buf, &end)) {Report("topic0"); return FALSE;}
765
766   buf += 9;
767   topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
768
769   for (i = 0; i < topic.wMapLen; i++)
770     {
771       ptr = buf + i * 0x1000;
772
773       /* I don't know why, it's necessary for printman.hlp */
774       if (ptr + 0x44 > end) ptr = end - 0x44;
775
776       newsize += HLPFILE_Uncompressed1_Size(ptr + 0xc, min(end, ptr + 0x1000));
777     }
778
779   topic.hMap    = GlobalAlloc(GMEM_FIXED, topic.wMapLen * sizeof(topic.map[0]));
780   topic.hBuffer = GlobalAlloc(GMEM_FIXED, newsize);
781   if (!topic.hMap || !topic.hBuffer) return FALSE;
782   topic.map = GlobalLock(topic.hMap);
783   newptr = GlobalLock(topic.hBuffer);
784   topic.end = newptr + newsize;
785
786   for (i = 0; i < topic.wMapLen; i++)
787     {
788       ptr = buf + i * 0x1000;
789       if (ptr + 0x44 > end) ptr = end - 0x44;
790
791       topic.map[i] = newptr - 0xc;
792       newptr = HLPFILE_Uncompress1(ptr + 0xc, min(end, ptr + 0x1000), newptr);
793     }
794
795   return TRUE;
796 }
797
798 /***********************************************************************
799  *
800  *           HLPFILE_Uncompressed2_Size
801  */
802
803 static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end)
804 {
805   UINT wSize   = 0;
806
807   while (ptr < end && *ptr)
808     {
809       if (*ptr >= 0x20)
810         wSize++, ptr++;
811       else
812         {
813           BYTE *phptr, *phend;
814           UINT code  = 0x100 * ptr[0] + ptr[1];
815           UINT index = (code - 0x100) / 2;
816           BOOL space = code & 1;
817
818           if (index < phrases.num)
819             {
820               phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
821               phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
822
823               if (phend < phptr) Report("uncompress2a");
824
825               wSize += phend - phptr;
826               if (space) wSize++;
827             }
828           else Report("uncompress2b");
829
830           ptr += 2;
831         }
832     }
833
834   return(wSize + 1);
835 }
836
837 /***********************************************************************
838  *
839  *           HLPFILE_Uncompress2
840  */
841
842 static VOID HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr)
843 {
844   BYTE *ptr    = *pptr;
845
846   while (ptr < end && *ptr)
847     {
848       if (*ptr >= 0x20)
849         *newptr++ = *ptr++;
850       else
851         {
852           BYTE *phptr, *phend;
853           UINT code  = 0x100 * ptr[0] + ptr[1];
854           UINT index = (code - 0x100) / 2;
855           BOOL space = code & 1;
856
857           phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
858           phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
859
860           memcpy(newptr, phptr, phend - phptr);
861           newptr += phend - phptr;
862           if (space) *newptr++ = ' ';
863
864           ptr += 2;
865         }
866     }
867   *newptr  = '\0';
868   *pptr    = ptr;
869 }
870
871 /***********************************************************************
872  *
873  *           HLPFILE_GetContext
874  */
875
876 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
877 {
878   UINT i, j, clen, tlen;
879   BYTE *cbuf, *cptr, *cend, *tbuf, *tptr, *tend;
880
881   if (!HLPFILE_FindSubFile("CONTEXT", &cbuf, &cend)) {Report("context0"); return FALSE;}
882   if (cbuf + 0x37 > cend) {Report("context1"); return(FALSE);};
883   clen = GET_UINT(cbuf, 0x2b);
884   if (cbuf + 0x37 + 8 * hlpfile->wContextLen > cend) {Report("context2"); return(FALSE);};
885
886   if (!HLPFILE_FindSubFile("TTLBTREE", &tbuf, &tend)) {Report("ttlb0"); return FALSE;}
887   if (tbuf + 0x37 > tend) {Report("ttlb1"); return(FALSE);};
888   tlen = GET_UINT(tbuf, 0x2b);
889
890   hlpfile->hContext = GlobalAlloc(GMEM_FIXED, clen * sizeof(HLPFILE_CONTEXT));
891   if (!hlpfile->hContext) return FALSE;
892   hlpfile->Context = GlobalLock(hlpfile->hContext);
893   hlpfile->wContextLen = clen;
894
895   cptr = cbuf + 0x37;
896   for (i = 0; i < clen; i++, cptr += 8)
897     {
898       tptr = tbuf + 0x37;
899       for (j = 0; j < tlen; j++, tptr += 5 + strlen(tptr + 4))
900         {
901           if (tptr + 4 >= tend) {Report("ttlb2"); return(FALSE);};
902           if (GET_UINT(tptr, 0) == GET_UINT(cptr, 4)) break;
903         }
904       if (j >= tlen)
905         {
906           Report("ttlb3");
907           j = 0;
908         }
909       hlpfile->Context[i].lHash = GET_UINT(cptr, 0);
910       hlpfile->Context[i].wPage = j;
911     }
912
913   return TRUE;
914 }
915
916 /***********************************************************************
917  *
918  *           HLPFILE_DeleteParagraph
919  */
920
921 static VOID HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
922 {
923   if (!paragraph) return;
924
925   if (paragraph->link) GlobalFree(paragraph->link->hSelf);
926
927   HLPFILE_DeleteParagraph(paragraph->next);
928   GlobalFree(paragraph->hSelf);
929 }
930
931 /***********************************************************************
932  *
933  *           DeletePage
934  */
935
936 static VOID HLPFILE_DeletePage(HLPFILE_PAGE* page)
937 {
938   if (!page) return;
939
940   HLPFILE_DeletePage(page->next);
941   HLPFILE_DeleteParagraph(page->first_paragraph);
942   GlobalFree(page->hSelf);
943 }
944
945 /***********************************************************************
946  *
947  *           DeleteMacro
948  */
949
950 static VOID HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
951 {
952   if (!macro) return;
953
954   HLPFILE_DeleteMacro(macro->next);
955   GlobalFree(macro->hSelf);
956 }
957
958 /***********************************************************************
959  *
960  *           HLPFILE_FreeHlpFile
961  */
962
963 VOID HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
964 {
965   if (!hlpfile) return;
966   if (--hlpfile->wRefCount) return;
967
968   if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
969   if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
970   else first_hlpfile = 0;
971
972   HLPFILE_DeletePage(hlpfile->first_page);
973   HLPFILE_DeleteMacro(hlpfile->first_macro);
974   if (hlpfile->hContext) GlobalFree(hlpfile->hContext);
975   if (hlpfile->hTitle) GlobalFree(hlpfile->hTitle);
976   GlobalFree(hlpfile->hSelf);
977 }
978
979 /***********************************************************************
980  *
981  *           FreeHlpFilePage
982  */
983
984 VOID HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page)
985 {
986   if (!page) return;
987   HLPFILE_FreeHlpFile(page->file);
988 }
989
990 /* Local Variables:    */
991 /* c-file-style: "GNU" */
992 /* End:                */