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