4 * Copyright 1996 Ulrich Schmid
13 static void Report(LPCSTR str)
16 fprintf(stderr, "%s\n", str);
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)
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*);
39 static HLPFILE *first_hlpfile = 0;
40 static HGLOBAL hFileBuffer;
41 static BYTE *file_buffer;
70 /***********************************************************************
75 HLPFILE_PAGE *HLPFILE_Contents(LPCSTR lpszPath)
77 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
79 if (!hlpfile) return(0);
81 return(hlpfile->first_page);
84 /***********************************************************************
86 * HLPFILE_PageByNumber
89 HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
92 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
94 if (!hlpfile) return(0);
96 for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
101 /***********************************************************************
103 * HLPFILE_HlpFilePageByHash
106 HLPFILE_PAGE *HLPFILE_PageByHash(LPCSTR lpszPath, LONG lHash)
111 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
113 if (!hlpfile) return(0);
115 for (i = 0; i < hlpfile->wContextLen; i++)
116 if (hlpfile->Context[i].lHash == lHash) break;
118 if (i >= hlpfile->wContextLen)
120 HLPFILE_FreeHlpFile(hlpfile);
124 wNum = hlpfile->Context[i].wPage;
125 for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
130 /***********************************************************************
135 LONG HLPFILE_Hash(LPCSTR lpszContext)
139 while((c = *lpszContext++))
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;
153 /***********************************************************************
155 * HLPFILE_ReadHlpFile
158 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
163 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
164 if (!lstrcmp(hlpfile->lpszPath, lpszPath))
166 hlpfile->wRefCount++;
170 hHlpFile = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
171 if (!hHlpFile) return(0);
173 hlpfile = GlobalLock(hHlpFile);
174 hlpfile->hSelf = hHlpFile;
175 hlpfile->wRefCount = 1;
177 hlpfile->hContext = 0;
178 hlpfile->wContextLen = 0;
179 hlpfile->first_page = 0;
180 hlpfile->first_macro = 0;
182 hlpfile->next = first_hlpfile;
183 first_hlpfile = hlpfile;
184 if (hlpfile->next) hlpfile->next->prev = hlpfile;
186 hlpfile->lpszPath = GlobalLock(hHlpFile);
187 hlpfile->lpszPath += sizeof(HLPFILE);
188 strcpy(hlpfile->lpszPath, lpszPath);
190 phrases.hBuffer = topic.hBuffer = hFileBuffer = 0;
192 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
194 HLPFILE_FreeHlpFile(hlpfile);
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);
206 /***********************************************************************
208 * HLPFILE_DoReadHlpFile
211 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
218 hFile=OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH);
219 if (hFile == HFILE_ERROR) return FALSE;
221 ret = HLPFILE_ReadFileToBuffer(hFile);
223 if (!ret) return FALSE;
225 HLPFILE_SystemCommands(hlpfile);
226 if (!HLPFILE_Uncompress1_Phrases()) return FALSE;
227 if (!HLPFILE_Uncompress1_Topic()) return FALSE;
229 buf = topic.map[0] + 0xc;
230 while(buf + 0xc < topic.end)
232 BYTE *end = min(buf + GET_UINT(buf, 0), topic.end);
233 UINT next, index, offset;
238 if (!HLPFILE_AddPage(hlpfile, buf, end)) return(FALSE);
242 if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE);
246 if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE);
250 fprintf(stderr, "buf[0x14] = %x\n", buf[0x14]);
253 next = GET_UINT(buf, 0xc);
254 if (next == 0xffffffff) break;
257 offset = next & 0x3fff;
258 if (index > topic.wMapLen) {Report("maplen"); break;}
259 buf = topic.map[index] + offset;
262 return(HLPFILE_GetContext(hlpfile));
265 /***********************************************************************
270 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end)
273 HLPFILE_PAGE *page, **pageptr;
277 for (pageptr = &hlpfile->first_page; *pageptr; pageptr = &(*pageptr)->next)
280 if (buf + 0x31 > end) {Report("page1"); return(FALSE);};
281 title = buf + GET_UINT(buf, 0x10);
282 if (title > end) {Report("page2"); return(FALSE);};
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;
290 page->file = hlpfile;
292 page->first_paragraph = 0;
294 page->lpszTitle = GlobalLock(hPage);
295 page->lpszTitle += sizeof(HLPFILE_PAGE);
296 HLPFILE_Uncompress2(&title, end, page->lpszTitle);
298 page->wNumber = GET_UINT(buf, 0x21);
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;
311 /***********************************************************************
313 * HLPFILE_AddParagraph
316 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end)
320 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
323 BOOL format_header = TRUE;
324 BOOL format_end = FALSE;
327 if (!hlpfile->first_page) {Report("paragraph1"); return(FALSE);};
329 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
330 for (paragraphptr = &page->first_paragraph; *paragraphptr;
331 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
333 if (buf + 0x19 > end) {Report("paragraph2"); return(FALSE);};
335 if (buf[0x14] == 0x02) return TRUE;
337 text = buf + GET_UINT(buf, 0x10);
343 while (*format) format++;
349 if (buf[0x17] & 1) format++;
353 Report("paragraph3");
361 format_header = FALSE;
363 mask = GET_USHORT(format, 0);
367 for (i = 0; i < 10; i++, mask = mask >> 1)
371 BOOL twoargs = FALSE;
375 if (i == 9 && !twoargs)
380 prefix0 = prefix1 = '?';
384 prefix0 = prefix1 = 'x';
388 prefix0 = prefix1 = 'X';
413 if (twoargs) format += (*format & 1) ? 2 : 1;
418 for (; !format_header && text < end && format < end && !*text; text++)
423 attributes.wFont = GET_USHORT(format, 1);
428 attributes.wVSpace++;
433 attributes.wVSpace += 2 - attributes.wVBackSpace;
434 attributes.wVBackSpace = 0;
435 attributes.wIndent = 0;
440 attributes.wIndent++;
455 attributes.wVBackSpace++;
465 attributes.link.lpszPath = hlpfile->lpszPath;
466 attributes.link.lHash = GET_UINT(format, 1);
467 attributes.link.bPopup = !(*format & 1);
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);
479 if (buf[0x14] != 0x23 || GET_USHORT(format, 1) == 0xffff)
481 if (format_end) Report("format_end");
487 format_header = TRUE;
498 if (text > end || format > end) {Report("paragraph_end"); return(FALSE);};
499 if (text == end && !format_end) Report("text_end");
501 if (text == end) break;
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 = ¶graph->next;
508 paragraph->hSelf = hParagraph;
512 paragraph->lpszText = GlobalLock(hParagraph);
513 paragraph->lpszText += sizeof(HLPFILE_PARAGRAPH);
514 HLPFILE_Uncompress2(&text, end, paragraph->lpszText);
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)
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;
530 ptr = GlobalLock(handle);
531 ptr += sizeof(HLPFILE_LINK);
532 lstrcpy(ptr, (LPSTR) attributes.link.lpszPath);
534 paragraph->link->lpszPath = ptr;
535 paragraph->link->lHash = attributes.link.lHash;
536 paragraph->link->bPopup = attributes.link.bPopup;
539 attributes.bDebug = 0;
540 attributes.wVSpace = 0;
541 attributes.wHSpace = 0;
542 attributes.link.lpszPath = 0;
548 /***********************************************************************
550 * HLPFILE_ReadFileToBuffer
553 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
555 BYTE header[16], dummy[1];
558 if (_hread(hFile, header, 16) != 16) {Report("header"); return(FALSE);};
560 size = GET_UINT(header, 12);
561 hFileBuffer = GlobalAlloc(GMEM_FIXED, size + 1);
562 if (!hFileBuffer) return FALSE;
563 file_buffer = GlobalLock(hFileBuffer);
565 memcpy(file_buffer, header, 16);
566 if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
567 {Report("filesize1"); return(FALSE);};
569 if (_hread(hFile, dummy, 1) != 0) Report("filesize2");
571 file_buffer[size] = '0';
576 /***********************************************************************
578 * HLPFILE_FindSubFile
581 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
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;
587 while (ptr < end && ptr[0] == 0x7c)
589 BYTE *fname = ptr + 1;
590 ptr += strlen(ptr) + 1;
591 if (!lstrcmpi(fname, name))
593 *subbuf = file_buffer + GET_UINT(ptr, 0);
594 *subend = *subbuf + GET_UINT(*subbuf, 0);
595 if (file_buffer > *subbuf || *subbuf > *subend || *subend >= end)
607 /***********************************************************************
609 * HLPFILE_SystemCommands
611 static VOID HLPFILE_SystemCommands(HLPFILE* hlpfile)
613 BYTE *buf, *ptr, *end;
615 HLPFILE_MACRO *macro, **m;
618 hlpfile->lpszTitle = "";
620 if (!HLPFILE_FindSubFile("SYSTEM", &buf, &end)) return;
622 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
624 switch (GET_USHORT(ptr, 0))
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);
635 if (GET_USHORT(ptr, 2) != 1 || ptr[4] != 0) Report("system2");
639 if (GET_USHORT(ptr, 2) != 4 || GET_UINT(ptr, 4) != 0) Report("system3");
643 handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1);
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;
652 m = &hlpfile->first_macro;
653 while (*m) m = &(*m)->next;
663 /***********************************************************************
665 * HLPFILE_Uncompressed1_Size
668 static INT HLPFILE_Uncompressed1_Size(BYTE *ptr, BYTE *end)
675 for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
679 INT code = GET_USHORT(ptr, 0);
680 INT len = 3 + (code >> 12);
684 else newsize++, ptr++;
691 /***********************************************************************
693 * HLPFILE_Uncompress1
696 static BYTE *HLPFILE_Uncompress1(BYTE *ptr, BYTE *end, BYTE *newptr)
703 for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1)
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);
714 else *newptr++ = *ptr++;
721 /***********************************************************************
723 * HLPFILE_Uncompress1_Phrases
726 static BOOL HLPFILE_Uncompress1_Phrases()
728 UINT i, num, newsize;
729 BYTE *buf, *end, *newbuf;
731 if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) {Report("phrases0"); return FALSE;}
733 num = phrases.num = GET_USHORT(buf, 9);
734 if (buf + 2 * num + 0x13 >= end) {Report("uncompress1a"); return(FALSE);};
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);
742 memcpy(newbuf, buf + 0x11, 2 * num + 2);
743 HLPFILE_Uncompress1(buf + 0x13 + 2 * num, end, newbuf + 2 * num + 2);
745 for (i = 0; i < num; i++)
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);};
754 /***********************************************************************
756 * HLPFILE_Uncompress1_Topic
759 static BOOL HLPFILE_Uncompress1_Topic()
761 BYTE *buf, *ptr, *end, *newptr;
764 if (!HLPFILE_FindSubFile("TOPIC", &buf, &end)) {Report("topic0"); return FALSE;}
767 topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
769 for (i = 0; i < topic.wMapLen; i++)
771 ptr = buf + i * 0x1000;
773 /* I don't know why, it's necessary for printman.hlp */
774 if (ptr + 0x44 > end) ptr = end - 0x44;
776 newsize += HLPFILE_Uncompressed1_Size(ptr + 0xc, min(end, ptr + 0x1000));
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;
786 for (i = 0; i < topic.wMapLen; i++)
788 ptr = buf + i * 0x1000;
789 if (ptr + 0x44 > end) ptr = end - 0x44;
791 topic.map[i] = newptr - 0xc;
792 newptr = HLPFILE_Uncompress1(ptr + 0xc, min(end, ptr + 0x1000), newptr);
798 /***********************************************************************
800 * HLPFILE_Uncompressed2_Size
803 static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end)
807 while (ptr < end && *ptr)
814 UINT code = 0x100 * ptr[0] + ptr[1];
815 UINT index = (code - 0x100) / 2;
816 BOOL space = code & 1;
818 if (index < phrases.num)
820 phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
821 phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
823 if (phend < phptr) Report("uncompress2a");
825 wSize += phend - phptr;
828 else Report("uncompress2b");
837 /***********************************************************************
839 * HLPFILE_Uncompress2
842 static VOID HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr)
846 while (ptr < end && *ptr)
853 UINT code = 0x100 * ptr[0] + ptr[1];
854 UINT index = (code - 0x100) / 2;
855 BOOL space = code & 1;
857 phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index);
858 phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2);
860 memcpy(newptr, phptr, phend - phptr);
861 newptr += phend - phptr;
862 if (space) *newptr++ = ' ';
871 /***********************************************************************
876 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
878 UINT i, j, clen, tlen;
879 BYTE *cbuf, *cptr, *cend, *tbuf, *tptr, *tend;
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);};
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);
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;
896 for (i = 0; i < clen; i++, cptr += 8)
899 for (j = 0; j < tlen; j++, tptr += 5 + strlen(tptr + 4))
901 if (tptr + 4 >= tend) {Report("ttlb2"); return(FALSE);};
902 if (GET_UINT(tptr, 0) == GET_UINT(cptr, 4)) break;
909 hlpfile->Context[i].lHash = GET_UINT(cptr, 0);
910 hlpfile->Context[i].wPage = j;
916 /***********************************************************************
918 * HLPFILE_DeleteParagraph
921 static VOID HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
923 if (!paragraph) return;
925 if (paragraph->link) GlobalFree(paragraph->link->hSelf);
927 HLPFILE_DeleteParagraph(paragraph->next);
928 GlobalFree(paragraph->hSelf);
931 /***********************************************************************
936 static VOID HLPFILE_DeletePage(HLPFILE_PAGE* page)
940 HLPFILE_DeletePage(page->next);
941 HLPFILE_DeleteParagraph(page->first_paragraph);
942 GlobalFree(page->hSelf);
945 /***********************************************************************
950 static VOID HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
954 HLPFILE_DeleteMacro(macro->next);
955 GlobalFree(macro->hSelf);
958 /***********************************************************************
960 * HLPFILE_FreeHlpFile
963 VOID HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
965 if (!hlpfile) return;
966 if (--hlpfile->wRefCount) return;
968 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
969 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
970 else first_hlpfile = 0;
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);
979 /***********************************************************************
984 VOID HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page)
987 HLPFILE_FreeHlpFile(page->file);
990 /* Local Variables: */
991 /* c-file-style: "GNU" */