4 * Copyright 1996 Ulrich Schmid
11 static void Report(LPCSTR str)
14 fprintf(stderr, "%s\n", str);
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)
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*);
37 static HLPFILE *first_hlpfile = 0;
38 static HGLOBAL hFileBuffer;
39 static BYTE *file_buffer;
68 /***********************************************************************
73 HLPFILE_PAGE *HLPFILE_Contents(LPCSTR lpszPath)
75 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
77 if (!hlpfile) return(0);
79 return(hlpfile->first_page);
82 /***********************************************************************
84 * HLPFILE_PageByNumber
87 HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
90 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
92 if (!hlpfile) return(0);
94 for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
99 /***********************************************************************
101 * HLPFILE_HlpFilePageByHash
104 HLPFILE_PAGE *HLPFILE_PageByHash(LPCSTR lpszPath, LONG lHash)
109 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
111 if (!hlpfile) return(0);
113 for (i = 0; i < hlpfile->wContextLen; i++)
114 if (hlpfile->Context[i].lHash == lHash) break;
116 if (i >= hlpfile->wContextLen)
118 HLPFILE_FreeHlpFile(hlpfile);
122 wNum = hlpfile->Context[i].wPage;
123 for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
128 /***********************************************************************
133 LONG HLPFILE_Hash(LPCSTR lpszContext)
137 while((c = *lpszContext++))
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;
151 /***********************************************************************
153 * HLPFILE_ReadHlpFile
156 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
161 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
162 if (!lstrcmp(hlpfile->lpszPath, lpszPath))
164 hlpfile->wRefCount++;
168 hHlpFile = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
169 if (!hHlpFile) return(0);
171 hlpfile = GlobalLock(hHlpFile);
172 hlpfile->hSelf = hHlpFile;
173 hlpfile->wRefCount = 1;
175 hlpfile->hContext = 0;
176 hlpfile->wContextLen = 0;
177 hlpfile->first_page = 0;
178 hlpfile->first_macro = 0;
180 hlpfile->next = first_hlpfile;
181 first_hlpfile = hlpfile;
182 if (hlpfile->next) hlpfile->next->prev = hlpfile;
184 hlpfile->lpszPath = GlobalLock(hHlpFile);
185 hlpfile->lpszPath += sizeof(HLPFILE);
186 strcpy(hlpfile->lpszPath, lpszPath);
188 phrases.hBuffer = topic.hBuffer = hFileBuffer = 0;
190 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
192 HLPFILE_FreeHlpFile(hlpfile);
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);
204 /***********************************************************************
206 * HLPFILE_DoReadHlpFile
209 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
216 hFile=OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH);
217 if (hFile == HFILE_ERROR) return FALSE;
219 ret = HLPFILE_ReadFileToBuffer(hFile);
221 if (!ret) return FALSE;
223 HLPFILE_SystemCommands(hlpfile);
224 if (!HLPFILE_Uncompress1_Phrases()) return FALSE;
225 if (!HLPFILE_Uncompress1_Topic()) return FALSE;
227 buf = topic.map[0] + 0xc;
228 while(buf + 0xc < topic.end)
230 BYTE *end = MIN(buf + GET_UINT(buf, 0), topic.end);
231 UINT next, index, offset;
236 if (!HLPFILE_AddPage(hlpfile, buf, end)) return(FALSE);
240 if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE);
244 if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE);
248 fprintf(stderr, "buf[0x14] = %x\n", buf[0x14]);
251 next = GET_UINT(buf, 0xc);
252 if (next == 0xffffffff) break;
255 offset = next & 0x3fff;
256 if (index > topic.wMapLen) {Report("maplen"); break;}
257 buf = topic.map[index] + offset;
260 return(HLPFILE_GetContext(hlpfile));
263 /***********************************************************************
268 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end)
271 HLPFILE_PAGE *page, **pageptr;
275 for (pageptr = &hlpfile->first_page; *pageptr; pageptr = &(*pageptr)->next)
278 if (buf + 0x31 > end) {Report("page1"); return(FALSE);};
279 title = buf + GET_UINT(buf, 0x10);
280 if (title > end) {Report("page2"); return(FALSE);};
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;
288 page->file = hlpfile;
290 page->first_paragraph = 0;
292 page->lpszTitle = GlobalLock(hPage);
293 page->lpszTitle += sizeof(HLPFILE_PAGE);
294 HLPFILE_Uncompress2(&title, end, page->lpszTitle);
296 page->wNumber = GET_UINT(buf, 0x21);
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;
309 /***********************************************************************
311 * HLPFILE_AddParagraph
314 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end)
318 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
321 BOOL format_header = TRUE;
322 BOOL format_end = FALSE;
325 if (!hlpfile->first_page) {Report("paragraph1"); return(FALSE);};
327 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
328 for (paragraphptr = &page->first_paragraph; *paragraphptr;
329 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
331 if (buf + 0x19 > end) {Report("paragraph2"); return(FALSE);};
333 if (buf[0x14] == 0x02) return TRUE;
335 text = buf + GET_UINT(buf, 0x10);
341 while (*format) format++;
347 if (buf[0x17] & 1) format++;
351 Report("paragraph3");
359 format_header = FALSE;
361 mask = GET_USHORT(format, 0);
365 for (i = 0; i < 10; i++, mask = mask >> 1)
369 BOOL twoargs = FALSE;
373 if (i == 9 && !twoargs)
378 prefix0 = prefix1 = '?';
382 prefix0 = prefix1 = 'x';
386 prefix0 = prefix1 = 'X';
411 if (twoargs) format += (*format & 1) ? 2 : 1;
416 for (; !format_header && text < end && format < end && !*text; text++)
421 attributes.wFont = GET_USHORT(format, 1);
426 attributes.wVSpace++;
431 attributes.wVSpace += 2 - attributes.wVBackSpace;
432 attributes.wVBackSpace = 0;
433 attributes.wIndent = 0;
438 attributes.wIndent++;
453 attributes.wVBackSpace++;
463 attributes.link.lpszPath = hlpfile->lpszPath;
464 attributes.link.lHash = GET_UINT(format, 1);
465 attributes.link.bPopup = !(*format & 1);
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);
477 if (buf[0x14] != 0x23 || GET_USHORT(format, 1) == 0xffff)
479 if (format_end) Report("format_end");
485 format_header = TRUE;
496 if (text > end || format > end) {Report("paragraph_end"); return(FALSE);};
497 if (text == end && !format_end) Report("text_end");
499 if (text == end) break;
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 = ¶graph->next;
506 paragraph->hSelf = hParagraph;
510 paragraph->lpszText = GlobalLock(hParagraph);
511 paragraph->lpszText += sizeof(HLPFILE_PARAGRAPH);
512 HLPFILE_Uncompress2(&text, end, paragraph->lpszText);
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)
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;
528 ptr = GlobalLock(handle);
529 ptr += sizeof(HLPFILE_LINK);
530 lstrcpy(ptr, (LPSTR) attributes.link.lpszPath);
532 paragraph->link->lpszPath = ptr;
533 paragraph->link->lHash = attributes.link.lHash;
534 paragraph->link->bPopup = attributes.link.bPopup;
537 attributes.bDebug = 0;
538 attributes.wVSpace = 0;
539 attributes.wHSpace = 0;
540 attributes.link.lpszPath = 0;
546 /***********************************************************************
548 * HLPFILE_ReadFileToBuffer
551 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
553 BYTE header[16], dummy[1];
556 if (_hread(hFile, header, 16) != 16) {Report("header"); return(FALSE);};
558 size = GET_UINT(header, 12);
559 hFileBuffer = GlobalAlloc(GMEM_FIXED, size + 1);
560 if (!hFileBuffer) return FALSE;
561 file_buffer = GlobalLock(hFileBuffer);
563 memcpy(file_buffer, header, 16);
564 if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
565 {Report("filesize1"); return(FALSE);};
567 if (_hread(hFile, dummy, 1) != 0) Report("filesize2");
569 file_buffer[size] = '0';
574 /***********************************************************************
576 * HLPFILE_FindSubFile
579 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
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;
585 while (ptr < end && ptr[0] == 0x7c)
587 BYTE *fname = ptr + 1;
588 ptr += strlen(ptr) + 1;
589 if (!lstrcmpi(fname, name))
591 *subbuf = file_buffer + GET_UINT(ptr, 0);
592 *subend = *subbuf + GET_UINT(*subbuf, 0);
593 if (file_buffer > *subbuf || *subbuf > *subend || *subend >= end)
605 /***********************************************************************
607 * HLPFILE_SystemCommands
609 static VOID HLPFILE_SystemCommands(HLPFILE* hlpfile)
611 BYTE *buf, *ptr, *end;
613 HLPFILE_MACRO *macro, **m;
616 hlpfile->lpszTitle = "";
618 if (!HLPFILE_FindSubFile("SYSTEM", &buf, &end)) return;
620 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
622 switch (GET_USHORT(ptr, 0))
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);
633 if (GET_USHORT(ptr, 2) != 1 || ptr[4] != 0) Report("system2");
637 if (GET_USHORT(ptr, 2) != 4 || GET_UINT(ptr, 4) != 0) Report("system3");
641 handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1);
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;
650 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
660 /***********************************************************************
662 * HLPFILE_Uncompressed1_Size
665 static INT HLPFILE_Uncompressed1_Size(BYTE *ptr, BYTE *end)
672 for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
676 INT code = GET_USHORT(ptr, 0);
677 INT len = 3 + (code >> 12);
681 else newsize++, ptr++;
688 /***********************************************************************
690 * HLPFILE_Uncompress1
693 static BYTE *HLPFILE_Uncompress1(BYTE *ptr, BYTE *end, BYTE *newptr)
700 for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
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);
711 else *newptr++ = *ptr++;
718 /***********************************************************************
720 * HLPFILE_Uncompress1_Phrases
723 static BOOL HLPFILE_Uncompress1_Phrases()
725 UINT i, num, newsize;
726 BYTE *buf, *end, *newbuf;
728 if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) {Report("phrases0"); return FALSE;}
730 num = phrases.num = GET_USHORT(buf, 9);
731 if (buf + 2 * num + 0x13 >= end) {Report("uncompress1a"); return(FALSE);};
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);
739 hmemcpy(newbuf, buf + 0x11, 2 * num + 2);
740 HLPFILE_Uncompress1(buf + 0x13 + 2 * num, end, newbuf + 2 * num + 2);
742 for (i = 0; i < num; i++)
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);};
751 /***********************************************************************
753 * HLPFILE_Uncompress1_Topic
756 static BOOL HLPFILE_Uncompress1_Topic()
758 BYTE *buf, *ptr, *end, *newptr;
761 if (!HLPFILE_FindSubFile("TOPIC", &buf, &end)) {Report("topic0"); return FALSE;}
764 topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
766 for (i = 0; i < topic.wMapLen; i++)
768 ptr = buf + i * 0x1000;
770 /* I don't know why, it's necessary for printman.hlp */
771 if (ptr + 0x44 > end) ptr = end - 0x44;
773 newsize += HLPFILE_Uncompressed1_Size(ptr + 0xc, MIN(end, ptr + 0x1000));
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;
783 for (i = 0; i < topic.wMapLen; i++)
785 ptr = buf + i * 0x1000;
786 if (ptr + 0x44 > end) ptr = end - 0x44;
788 topic.map[i] = newptr - 0xc;
789 newptr = HLPFILE_Uncompress1(ptr + 0xc, MIN(end, ptr + 0x1000), newptr);
795 /***********************************************************************
797 * HLPFILE_Uncompressed2_Size
800 static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end)
804 while (ptr < end && *ptr)
811 UINT code = 0x100 * ptr[0] + ptr[1];
812 UINT index = (code - 0x100) / 2;
813 BOOL space = code & 1;
815 if (index < phrases.num)
817 phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
818 phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
820 if (phend < phptr) Report("uncompress2a");
822 wSize += phend - phptr;
825 else Report("uncompress2b");
834 /***********************************************************************
836 * HLPFILE_Uncompress2
839 static VOID HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr)
843 while (ptr < end && *ptr)
850 UINT code = 0x100 * ptr[0] + ptr[1];
851 UINT index = (code - 0x100) / 2;
852 BOOL space = code & 1;
854 phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
855 phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
857 hmemcpy(newptr, phptr, phend - phptr);
858 newptr += phend - phptr;
859 if (space) *newptr++ = ' ';
868 /***********************************************************************
873 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
875 UINT i, j, clen, tlen;
876 BYTE *cbuf, *cptr, *cend, *tbuf, *tptr, *tend;
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);};
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);
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;
893 for (i = 0; i < clen; i++, cptr += 8)
896 for (j = 0; j < tlen; j++, tptr += 5 + strlen(tptr + 4))
898 if (tptr + 4 >= tend) {Report("ttlb2"); return(FALSE);};
899 if (GET_UINT(tptr, 0) == GET_UINT(cptr, 4)) break;
906 hlpfile->Context[i].lHash = GET_UINT(cptr, 0);
907 hlpfile->Context[i].wPage = j;
913 /***********************************************************************
915 * HLPFILE_DeleteParagraph
918 static VOID HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
920 if (!paragraph) return;
922 if (paragraph->link) GlobalFree(paragraph->link->hSelf);
924 HLPFILE_DeleteParagraph(paragraph->next);
925 GlobalFree(paragraph->hSelf);
928 /***********************************************************************
933 static VOID HLPFILE_DeletePage(HLPFILE_PAGE* page)
937 HLPFILE_DeletePage(page->next);
938 HLPFILE_DeleteParagraph(page->first_paragraph);
939 GlobalFree(page->hSelf);
942 /***********************************************************************
947 static VOID HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
951 HLPFILE_DeleteMacro(macro->next);
952 GlobalFree(macro->hSelf);
955 /***********************************************************************
957 * HLPFILE_FreeHlpFile
960 VOID HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
962 if (!hlpfile) return;
963 if (--hlpfile->wRefCount) return;
965 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
966 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
967 else first_hlpfile = 0;
969 HLPFILE_DeletePage(hlpfile->first_page);
970 HLPFILE_DeleteMacro(hlpfile->first_macro);
971 if (hlpfile->hContext) GlobalFree(hlpfile->hContext);
972 if (hlpfile->hTitle) GlobalFree(hlpfile->hTitle);
973 GlobalFree(hlpfile->hSelf);
976 /***********************************************************************
981 VOID HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page)
984 HLPFILE_FreeHlpFile(page->file);
987 /* Local Variables: */
988 /* c-file-style: "GNU" */