| /* | |
| * Help Viewer | |
| * | |
| * Copyright 1996 Ulrich Schmid | |
| */ | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include "windows.h" | |
| #include "windowsx.h" | |
| #include "winhelp.h" | |
| static void Report(LPCSTR str) | |
| { | |
| #if 0 | |
| fprintf(stderr, "%s\n", str); | |
| #endif | |
| } | |
| #define GET_USHORT(buffer, i)\ | |
| (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1]))) | |
| #define GET_SHORT(buffer, i)\ | |
| (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1]))) | |
| #define GET_UINT(buffer, i)\ | |
| GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i+2) | |
| static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR); | |
| static BOOL HLPFILE_ReadFileToBuffer(HFILE); | |
| static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**); | |
| static VOID HLPFILE_SystemCommands(HLPFILE*); | |
| static BOOL HLPFILE_Uncompress1_Phrases(); | |
| static BOOL HLPFILE_Uncompress1_Topic(); | |
| static BOOL HLPFILE_GetContext(HLPFILE*); | |
| static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*); | |
| static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*); | |
| static UINT HLPFILE_Uncompressed2_Size(BYTE*, BYTE*); | |
| static VOID HLPFILE_Uncompress2(BYTE**, BYTE*, BYTE*); | |
| static HLPFILE *first_hlpfile = 0; | |
| static HGLOBAL hFileBuffer; | |
| static BYTE *file_buffer; | |
| static struct | |
| { | |
| UINT num; | |
| BYTE *buf; | |
| HGLOBAL hBuffer; | |
| } phrases; | |
| static struct | |
| { | |
| BYTE **map; | |
| BYTE *end; | |
| UINT wMapLen; | |
| HGLOBAL hMap; | |
| HGLOBAL hBuffer; | |
| } topic; | |
| static struct | |
| { | |
| UINT bDebug; | |
| UINT wFont; | |
| UINT wIndent; | |
| UINT wHSpace; | |
| UINT wVSpace; | |
| UINT wVBackSpace; | |
| HLPFILE_LINK link; | |
| } attributes; | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Contents | |
| */ | |
| HLPFILE_PAGE *HLPFILE_Contents(LPCSTR lpszPath) | |
| { | |
| HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); | |
| if (!hlpfile) return(0); | |
| return(hlpfile->first_page); | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_PageByNumber | |
| */ | |
| HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum) | |
| { | |
| HLPFILE_PAGE *page; | |
| HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); | |
| if (!hlpfile) return(0); | |
| for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--; | |
| return page; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_HlpFilePageByHash | |
| */ | |
| HLPFILE_PAGE *HLPFILE_PageByHash(LPCSTR lpszPath, LONG lHash) | |
| { | |
| INT i; | |
| UINT wNum; | |
| HLPFILE_PAGE *page; | |
| HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); | |
| if (!hlpfile) return(0); | |
| for (i = 0; i < hlpfile->wContextLen; i++) | |
| if (hlpfile->Context[i].lHash == lHash) break; | |
| if (i >= hlpfile->wContextLen) | |
| { | |
| HLPFILE_FreeHlpFile(hlpfile); | |
| return(0); | |
| } | |
| wNum = hlpfile->Context[i].wPage; | |
| for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--; | |
| return page; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Hash | |
| */ | |
| LONG HLPFILE_Hash(LPCSTR lpszContext) | |
| { | |
| LONG lHash = 0; | |
| CHAR c; | |
| while((c = *lpszContext++)) | |
| { | |
| CHAR x = 0; | |
| if (c >= 'A' && c <= 'Z') x = c - 'A' + 17; | |
| if (c >= 'a' && c <= 'z') x = c - 'a' + 17; | |
| if (c >= '1' && c <= '9') x = c - '0'; | |
| if (c == '0') x = 10; | |
| if (c == '.') x = 12; | |
| if (c == '_') x = 13; | |
| if (x) lHash = lHash * 43 + x; | |
| } | |
| return lHash; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_ReadHlpFile | |
| */ | |
| HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath) | |
| { | |
| HGLOBAL hHlpFile; | |
| HLPFILE *hlpfile; | |
| for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next) | |
| if (!lstrcmp(hlpfile->lpszPath, lpszPath)) | |
| { | |
| hlpfile->wRefCount++; | |
| return(hlpfile); | |
| } | |
| hHlpFile = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE) + lstrlen(lpszPath) + 1); | |
| if (!hHlpFile) return(0); | |
| hlpfile = GlobalLock(hHlpFile); | |
| hlpfile->hSelf = hHlpFile; | |
| hlpfile->wRefCount = 1; | |
| hlpfile->hTitle = 0; | |
| hlpfile->hContext = 0; | |
| hlpfile->wContextLen = 0; | |
| hlpfile->first_page = 0; | |
| hlpfile->first_macro = 0; | |
| hlpfile->prev = 0; | |
| hlpfile->next = first_hlpfile; | |
| first_hlpfile = hlpfile; | |
| if (hlpfile->next) hlpfile->next->prev = hlpfile; | |
| hlpfile->lpszPath = GlobalLock(hHlpFile); | |
| hlpfile->lpszPath += sizeof(HLPFILE); | |
| strcpy(hlpfile->lpszPath, lpszPath); | |
| phrases.hBuffer = topic.hBuffer = hFileBuffer = 0; | |
| if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath)) | |
| { | |
| HLPFILE_FreeHlpFile(hlpfile); | |
| hlpfile = 0; | |
| } | |
| if (phrases.hBuffer) GlobalFree(phrases.hBuffer); | |
| if (topic.hBuffer) GlobalFree(topic.hBuffer); | |
| if (topic.hMap) GlobalFree(topic.hMap); | |
| if (hFileBuffer) GlobalFree(hFileBuffer); | |
| return(hlpfile); | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_DoReadHlpFile | |
| */ | |
| static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath) | |
| { | |
| BOOL ret; | |
| HFILE hFile; | |
| OFSTRUCT ofs; | |
| BYTE *buf; | |
| hFile=OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH); | |
| if (hFile == HFILE_ERROR) return FALSE; | |
| ret = HLPFILE_ReadFileToBuffer(hFile); | |
| _lclose(hFile); | |
| if (!ret) return FALSE; | |
| HLPFILE_SystemCommands(hlpfile); | |
| if (!HLPFILE_Uncompress1_Phrases()) return FALSE; | |
| if (!HLPFILE_Uncompress1_Topic()) return FALSE; | |
| buf = topic.map[0] + 0xc; | |
| while(buf + 0xc < topic.end) | |
| { | |
| BYTE *end = min(buf + GET_UINT(buf, 0), topic.end); | |
| UINT next, index, offset; | |
| switch (buf[0x14]) | |
| { | |
| case 0x02: | |
| if (!HLPFILE_AddPage(hlpfile, buf, end)) return(FALSE); | |
| break; | |
| case 0x20: | |
| if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE); | |
| break; | |
| case 0x23: | |
| if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE); | |
| break; | |
| default: | |
| fprintf(stderr, "buf[0x14] = %x\n", buf[0x14]); | |
| } | |
| next = GET_UINT(buf, 0xc); | |
| if (next == 0xffffffff) break; | |
| index = next >> 14; | |
| offset = next & 0x3fff; | |
| if (index > topic.wMapLen) {Report("maplen"); break;} | |
| buf = topic.map[index] + offset; | |
| } | |
| return(HLPFILE_GetContext(hlpfile)); | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_AddPage | |
| */ | |
| static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end) | |
| { | |
| HGLOBAL hPage; | |
| HLPFILE_PAGE *page, **pageptr; | |
| BYTE *title; | |
| UINT titlesize; | |
| for (pageptr = &hlpfile->first_page; *pageptr; pageptr = &(*pageptr)->next) | |
| /* Nothing */; | |
| if (buf + 0x31 > end) {Report("page1"); return(FALSE);}; | |
| title = buf + GET_UINT(buf, 0x10); | |
| if (title > end) {Report("page2"); return(FALSE);}; | |
| titlesize = HLPFILE_Uncompressed2_Size(title, end); | |
| hPage = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PAGE) + titlesize); | |
| if (!hPage) return FALSE; | |
| page = *pageptr = GlobalLock(hPage); | |
| pageptr = &page->next; | |
| page->hSelf = hPage; | |
| page->file = hlpfile; | |
| page->next = 0; | |
| page->first_paragraph = 0; | |
| page->lpszTitle = GlobalLock(hPage); | |
| page->lpszTitle += sizeof(HLPFILE_PAGE); | |
| HLPFILE_Uncompress2(&title, end, page->lpszTitle); | |
| page->wNumber = GET_UINT(buf, 0x21); | |
| attributes.bDebug = 0; | |
| attributes.wFont = 0; | |
| attributes.wVSpace = 0; | |
| attributes.wVBackSpace = 0; | |
| attributes.wHSpace = 0; | |
| attributes.wIndent = 0; | |
| attributes.link.lpszPath = 0; | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_AddParagraph | |
| */ | |
| static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end) | |
| { | |
| HGLOBAL hParagraph; | |
| HLPFILE_PAGE *page; | |
| HLPFILE_PARAGRAPH *paragraph, **paragraphptr; | |
| UINT textsize; | |
| BYTE *format, *text; | |
| BOOL format_header = TRUE; | |
| BOOL format_end = FALSE; | |
| UINT mask, i; | |
| if (!hlpfile->first_page) {Report("paragraph1"); return(FALSE);}; | |
| for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */; | |
| for (paragraphptr = &page->first_paragraph; *paragraphptr; | |
| paragraphptr = &(*paragraphptr)->next) /* Nothing */; | |
| if (buf + 0x19 > end) {Report("paragraph2"); return(FALSE);}; | |
| if (buf[0x14] == 0x02) return TRUE; | |
| text = buf + GET_UINT(buf, 0x10); | |
| switch (buf[0x14]) | |
| { | |
| case 0x20: | |
| format = buf + 0x18; | |
| while (*format) format++; | |
| format += 4; | |
| break; | |
| case 0x23: | |
| format = buf + 0x2b; | |
| if (buf[0x17] & 1) format++; | |
| break; | |
| default: | |
| Report("paragraph3"); | |
| return FALSE; | |
| } | |
| while (text < end) | |
| { | |
| if (format_header) | |
| { | |
| format_header = FALSE; | |
| mask = GET_USHORT(format, 0); | |
| mask &= 0x3ff; | |
| format += 2; | |
| for (i = 0; i < 10; i++, mask = mask >> 1) | |
| { | |
| if (mask & 1) | |
| { | |
| BOOL twoargs = FALSE; | |
| CHAR prefix0 = ' '; | |
| CHAR prefix1 = '*'; | |
| if (i == 9 && !twoargs) | |
| { | |
| switch (*format++) | |
| { | |
| default: | |
| prefix0 = prefix1 = '?'; | |
| break; | |
| case 0x82: | |
| prefix0 = prefix1 = 'x'; | |
| break; | |
| case 0x84: | |
| prefix0 = prefix1 = 'X'; | |
| twoargs = TRUE; | |
| } | |
| } | |
| if (*format & 1) | |
| switch(*format) | |
| { | |
| default: | |
| format += 2; | |
| break; | |
| } | |
| else | |
| switch(*format) | |
| { | |
| default: | |
| format++; | |
| break; | |
| case 0x08: | |
| format += 3; | |
| break; | |
| } | |
| if (twoargs) format += (*format & 1) ? 2 : 1; | |
| } | |
| } | |
| } | |
| for (; !format_header && text < end && format < end && !*text; text++) | |
| { | |
| switch(*format) | |
| { | |
| case 0x80: | |
| attributes.wFont = GET_USHORT(format, 1); | |
| format += 3; | |
| break; | |
| case 0x81: | |
| attributes.wVSpace++; | |
| format += 1; | |
| break; | |
| case 0x82: | |
| attributes.wVSpace += 2 - attributes.wVBackSpace; | |
| attributes.wVBackSpace = 0; | |
| attributes.wIndent = 0; | |
| format += 1; | |
| break; | |
| case 0x83: | |
| attributes.wIndent++; | |
| format += 1; | |
| break; | |
| case 0x84: | |
| format += 3; | |
| break; | |
| case 0x86: | |
| case 0x87: | |
| case 0x88: | |
| format += 9; | |
| break; | |
| case 0x89: | |
| attributes.wVBackSpace++; | |
| format += 1; | |
| break; | |
| case 0xa9: | |
| format += 2; | |
| break; | |
| case 0xe2: | |
| case 0xe3: | |
| attributes.link.lpszPath = hlpfile->lpszPath; | |
| attributes.link.lHash = GET_UINT(format, 1); | |
| attributes.link.bPopup = !(*format & 1); | |
| format += 5; | |
| break; | |
| case 0xea: | |
| attributes.link.lpszPath = format + 8; | |
| attributes.link.lHash = GET_UINT(format, 4); | |
| attributes.link.bPopup = !(*format & 1); | |
| format += 3 + GET_USHORT(format, 1); | |
| break; | |
| case 0xff: | |
| if (buf[0x14] != 0x23 || GET_USHORT(format, 1) == 0xffff) | |
| { | |
| if (format_end) Report("format_end"); | |
| format_end = TRUE; | |
| break; | |
| } | |
| else | |
| { | |
| format_header = TRUE; | |
| format += 10; | |
| break; | |
| } | |
| default: | |
| Report("format"); | |
| format++; | |
| } | |
| } | |
| if (text > end || format > end) {Report("paragraph_end"); return(FALSE);}; | |
| if (text == end && !format_end) Report("text_end"); | |
| if (text == end) break; | |
| textsize = HLPFILE_Uncompressed2_Size(text, end); | |
| hParagraph = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PARAGRAPH) + textsize); | |
| if (!hParagraph) return FALSE; | |
| paragraph = *paragraphptr = GlobalLock(hParagraph); | |
| paragraphptr = ¶graph->next; | |
| paragraph->hSelf = hParagraph; | |
| paragraph->next = 0; | |
| paragraph->link = 0; | |
| paragraph->lpszText = GlobalLock(hParagraph); | |
| paragraph->lpszText += sizeof(HLPFILE_PARAGRAPH); | |
| HLPFILE_Uncompress2(&text, end, paragraph->lpszText); | |
| paragraph->bDebug = attributes.bDebug; | |
| paragraph->wFont = attributes.wFont; | |
| paragraph->wVSpace = attributes.wVSpace; | |
| paragraph->wHSpace = attributes.wHSpace; | |
| paragraph->wIndent = attributes.wIndent; | |
| if (attributes.link.lpszPath) | |
| { | |
| LPSTR ptr; | |
| HGLOBAL handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_LINK) + | |
| strlen(attributes.link.lpszPath) + 1); | |
| if (!handle) return FALSE; | |
| paragraph->link = GlobalLock(handle); | |
| paragraph->link->hSelf = handle; | |
| ptr = GlobalLock(handle); | |
| ptr += sizeof(HLPFILE_LINK); | |
| lstrcpy(ptr, (LPSTR) attributes.link.lpszPath); | |
| paragraph->link->lpszPath = ptr; | |
| paragraph->link->lHash = attributes.link.lHash; | |
| paragraph->link->bPopup = attributes.link.bPopup; | |
| } | |
| attributes.bDebug = 0; | |
| attributes.wVSpace = 0; | |
| attributes.wHSpace = 0; | |
| attributes.link.lpszPath = 0; | |
| } | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_ReadFileToBuffer | |
| */ | |
| static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile) | |
| { | |
| BYTE header[16], dummy[1]; | |
| UINT size; | |
| if (_hread(hFile, header, 16) != 16) {Report("header"); return(FALSE);}; | |
| size = GET_UINT(header, 12); | |
| hFileBuffer = GlobalAlloc(GMEM_FIXED, size + 1); | |
| if (!hFileBuffer) return FALSE; | |
| file_buffer = GlobalLock(hFileBuffer); | |
| memcpy(file_buffer, header, 16); | |
| if (_hread(hFile, file_buffer + 16, size - 16) != size - 16) | |
| {Report("filesize1"); return(FALSE);}; | |
| if (_hread(hFile, dummy, 1) != 0) Report("filesize2"); | |
| file_buffer[size] = '0'; | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_FindSubFile | |
| */ | |
| static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend) | |
| { | |
| BYTE *root = file_buffer + GET_UINT(file_buffer, 4); | |
| BYTE *end = file_buffer + GET_UINT(file_buffer, 12); | |
| BYTE *ptr = root + 0x37; | |
| while (ptr < end && ptr[0] == 0x7c) | |
| { | |
| BYTE *fname = ptr + 1; | |
| ptr += strlen(ptr) + 1; | |
| if (!lstrcmpi(fname, name)) | |
| { | |
| *subbuf = file_buffer + GET_UINT(ptr, 0); | |
| *subend = *subbuf + GET_UINT(*subbuf, 0); | |
| if (file_buffer > *subbuf || *subbuf > *subend || *subend >= end) | |
| { | |
| Report("subfile"); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| else ptr += 4; | |
| } | |
| return FALSE; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_SystemCommands | |
| */ | |
| static VOID HLPFILE_SystemCommands(HLPFILE* hlpfile) | |
| { | |
| BYTE *buf, *ptr, *end; | |
| HGLOBAL handle; | |
| HLPFILE_MACRO *macro, **m; | |
| LPSTR p; | |
| hlpfile->lpszTitle = ""; | |
| if (!HLPFILE_FindSubFile("SYSTEM", &buf, &end)) return; | |
| for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4) | |
| { | |
| switch (GET_USHORT(ptr, 0)) | |
| { | |
| case 1: | |
| if (hlpfile->hTitle) {Report("title"); break;} | |
| hlpfile->hTitle = GlobalAlloc(GMEM_FIXED, strlen(ptr + 4) + 1); | |
| if (!hlpfile->hTitle) return; | |
| hlpfile->lpszTitle = GlobalLock(hlpfile->hTitle); | |
| lstrcpy(hlpfile->lpszTitle, ptr + 4); | |
| break; | |
| case 2: | |
| if (GET_USHORT(ptr, 2) != 1 || ptr[4] != 0) Report("system2"); | |
| break; | |
| case 3: | |
| if (GET_USHORT(ptr, 2) != 4 || GET_UINT(ptr, 4) != 0) Report("system3"); | |
| break; | |
| case 4: | |
| handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1); | |
| if (!handle) break; | |
| macro = GlobalLock(handle); | |
| macro->hSelf = handle; | |
| p = GlobalLock(handle); | |
| p += sizeof(HLPFILE_MACRO); | |
| lstrcpy(p, (LPSTR) ptr + 4); | |
| macro->lpszMacro = p; | |
| macro->next = 0; | |
| m = &hlpfile->first_macro; | |
| while (*m) m = &(*m)->next; | |
| *m = macro; | |
| break; | |
| default: | |
| Report("system"); | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Uncompressed1_Size | |
| */ | |
| static INT HLPFILE_Uncompressed1_Size(BYTE *ptr, BYTE *end) | |
| { | |
| INT i, newsize = 0; | |
| while (ptr < end) | |
| { | |
| INT mask=*ptr++; | |
| for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1) | |
| { | |
| if (mask & 1) | |
| { | |
| INT code = GET_USHORT(ptr, 0); | |
| INT len = 3 + (code >> 12); | |
| newsize += len; | |
| ptr += 2; | |
| } | |
| else newsize++, ptr++; | |
| } | |
| } | |
| return(newsize); | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Uncompress1 | |
| */ | |
| static BYTE *HLPFILE_Uncompress1(BYTE *ptr, BYTE *end, BYTE *newptr) | |
| { | |
| INT i; | |
| while (ptr < end) | |
| { | |
| INT mask=*ptr++; | |
| for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1) | |
| { | |
| if (mask & 1) | |
| { | |
| INT code = GET_USHORT(ptr, 0); | |
| INT len = 3 + (code >> 12); | |
| INT offset = code & 0xfff; | |
| memcpy(newptr, newptr - offset - 1, len); | |
| newptr += len; | |
| ptr += 2; | |
| } | |
| else *newptr++ = *ptr++; | |
| } | |
| } | |
| return(newptr); | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Uncompress1_Phrases | |
| */ | |
| static BOOL HLPFILE_Uncompress1_Phrases() | |
| { | |
| UINT i, num, newsize; | |
| BYTE *buf, *end, *newbuf; | |
| if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) {Report("phrases0"); return FALSE;} | |
| num = phrases.num = GET_USHORT(buf, 9); | |
| if (buf + 2 * num + 0x13 >= end) {Report("uncompress1a"); return(FALSE);}; | |
| newsize = 2 * num + 2; | |
| newsize += HLPFILE_Uncompressed1_Size(buf + 0x13 + 2 * num, end); | |
| phrases.hBuffer = GlobalAlloc(GMEM_FIXED, newsize); | |
| if (!phrases.hBuffer) return FALSE; | |
| newbuf = phrases.buf = GlobalLock(phrases.hBuffer); | |
| memcpy(newbuf, buf + 0x11, 2 * num + 2); | |
| HLPFILE_Uncompress1(buf + 0x13 + 2 * num, end, newbuf + 2 * num + 2); | |
| for (i = 0; i < num; i++) | |
| { | |
| INT i0 = GET_USHORT(newbuf, 2 * i); | |
| INT i1 = GET_USHORT(newbuf, 2 * i + 2); | |
| if (i1 < i0 || i1 > newsize) {Report("uncompress1b"); return(FALSE);}; | |
| } | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Uncompress1_Topic | |
| */ | |
| static BOOL HLPFILE_Uncompress1_Topic() | |
| { | |
| BYTE *buf, *ptr, *end, *newptr; | |
| INT i, newsize = 0; | |
| if (!HLPFILE_FindSubFile("TOPIC", &buf, &end)) {Report("topic0"); return FALSE;} | |
| buf += 9; | |
| topic.wMapLen = (end - buf - 1) / 0x1000 + 1; | |
| for (i = 0; i < topic.wMapLen; i++) | |
| { | |
| ptr = buf + i * 0x1000; | |
| /* I don't know why, it's necessary for printman.hlp */ | |
| if (ptr + 0x44 > end) ptr = end - 0x44; | |
| newsize += HLPFILE_Uncompressed1_Size(ptr + 0xc, min(end, ptr + 0x1000)); | |
| } | |
| topic.hMap = GlobalAlloc(GMEM_FIXED, topic.wMapLen * sizeof(topic.map[0])); | |
| topic.hBuffer = GlobalAlloc(GMEM_FIXED, newsize); | |
| if (!topic.hMap || !topic.hBuffer) return FALSE; | |
| topic.map = GlobalLock(topic.hMap); | |
| newptr = GlobalLock(topic.hBuffer); | |
| topic.end = newptr + newsize; | |
| for (i = 0; i < topic.wMapLen; i++) | |
| { | |
| ptr = buf + i * 0x1000; | |
| if (ptr + 0x44 > end) ptr = end - 0x44; | |
| topic.map[i] = newptr - 0xc; | |
| newptr = HLPFILE_Uncompress1(ptr + 0xc, min(end, ptr + 0x1000), newptr); | |
| } | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Uncompressed2_Size | |
| */ | |
| static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end) | |
| { | |
| UINT wSize = 0; | |
| while (ptr < end && *ptr) | |
| { | |
| if (*ptr >= 0x20) | |
| wSize++, ptr++; | |
| else | |
| { | |
| BYTE *phptr, *phend; | |
| UINT code = 0x100 * ptr[0] + ptr[1]; | |
| UINT index = (code - 0x100) / 2; | |
| BOOL space = code & 1; | |
| if (index < phrases.num) | |
| { | |
| phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index); | |
| phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2); | |
| if (phend < phptr) Report("uncompress2a"); | |
| wSize += phend - phptr; | |
| if (space) wSize++; | |
| } | |
| else Report("uncompress2b"); | |
| ptr += 2; | |
| } | |
| } | |
| return(wSize + 1); | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_Uncompress2 | |
| */ | |
| static VOID HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr) | |
| { | |
| BYTE *ptr = *pptr; | |
| while (ptr < end && *ptr) | |
| { | |
| if (*ptr >= 0x20) | |
| *newptr++ = *ptr++; | |
| else | |
| { | |
| BYTE *phptr, *phend; | |
| UINT code = 0x100 * ptr[0] + ptr[1]; | |
| UINT index = (code - 0x100) / 2; | |
| BOOL space = code & 1; | |
| phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index); | |
| phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2); | |
| memcpy(newptr, phptr, phend - phptr); | |
| newptr += phend - phptr; | |
| if (space) *newptr++ = ' '; | |
| ptr += 2; | |
| } | |
| } | |
| *newptr = '\0'; | |
| *pptr = ptr; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_GetContext | |
| */ | |
| static BOOL HLPFILE_GetContext(HLPFILE *hlpfile) | |
| { | |
| UINT i, j, clen, tlen; | |
| BYTE *cbuf, *cptr, *cend, *tbuf, *tptr, *tend; | |
| if (!HLPFILE_FindSubFile("CONTEXT", &cbuf, &cend)) {Report("context0"); return FALSE;} | |
| if (cbuf + 0x37 > cend) {Report("context1"); return(FALSE);}; | |
| clen = GET_UINT(cbuf, 0x2b); | |
| if (cbuf + 0x37 + 8 * hlpfile->wContextLen > cend) {Report("context2"); return(FALSE);}; | |
| if (!HLPFILE_FindSubFile("TTLBTREE", &tbuf, &tend)) {Report("ttlb0"); return FALSE;} | |
| if (tbuf + 0x37 > tend) {Report("ttlb1"); return(FALSE);}; | |
| tlen = GET_UINT(tbuf, 0x2b); | |
| hlpfile->hContext = GlobalAlloc(GMEM_FIXED, clen * sizeof(HLPFILE_CONTEXT)); | |
| if (!hlpfile->hContext) return FALSE; | |
| hlpfile->Context = GlobalLock(hlpfile->hContext); | |
| hlpfile->wContextLen = clen; | |
| cptr = cbuf + 0x37; | |
| for (i = 0; i < clen; i++, cptr += 8) | |
| { | |
| tptr = tbuf + 0x37; | |
| for (j = 0; j < tlen; j++, tptr += 5 + strlen(tptr + 4)) | |
| { | |
| if (tptr + 4 >= tend) {Report("ttlb2"); return(FALSE);}; | |
| if (GET_UINT(tptr, 0) == GET_UINT(cptr, 4)) break; | |
| } | |
| if (j >= tlen) | |
| { | |
| Report("ttlb3"); | |
| j = 0; | |
| } | |
| hlpfile->Context[i].lHash = GET_UINT(cptr, 0); | |
| hlpfile->Context[i].wPage = j; | |
| } | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_DeleteParagraph | |
| */ | |
| static VOID HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph) | |
| { | |
| if (!paragraph) return; | |
| if (paragraph->link) GlobalFree(paragraph->link->hSelf); | |
| HLPFILE_DeleteParagraph(paragraph->next); | |
| GlobalFree(paragraph->hSelf); | |
| } | |
| /*********************************************************************** | |
| * | |
| * DeletePage | |
| */ | |
| static VOID HLPFILE_DeletePage(HLPFILE_PAGE* page) | |
| { | |
| if (!page) return; | |
| HLPFILE_DeletePage(page->next); | |
| HLPFILE_DeleteParagraph(page->first_paragraph); | |
| GlobalFree(page->hSelf); | |
| } | |
| /*********************************************************************** | |
| * | |
| * DeleteMacro | |
| */ | |
| static VOID HLPFILE_DeleteMacro(HLPFILE_MACRO* macro) | |
| { | |
| if (!macro) return; | |
| HLPFILE_DeleteMacro(macro->next); | |
| GlobalFree(macro->hSelf); | |
| } | |
| /*********************************************************************** | |
| * | |
| * HLPFILE_FreeHlpFile | |
| */ | |
| VOID HLPFILE_FreeHlpFile(HLPFILE* hlpfile) | |
| { | |
| if (!hlpfile) return; | |
| if (--hlpfile->wRefCount) return; | |
| if (hlpfile->next) hlpfile->next->prev = hlpfile->prev; | |
| if (hlpfile->prev) hlpfile->prev->next = hlpfile->next; | |
| else first_hlpfile = 0; | |
| HLPFILE_DeletePage(hlpfile->first_page); | |
| HLPFILE_DeleteMacro(hlpfile->first_macro); | |
| if (hlpfile->hContext) GlobalFree(hlpfile->hContext); | |
| if (hlpfile->hTitle) GlobalFree(hlpfile->hTitle); | |
| GlobalFree(hlpfile->hSelf); | |
| } | |
| /*********************************************************************** | |
| * | |
| * FreeHlpFilePage | |
| */ | |
| VOID HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page) | |
| { | |
| if (!page) return; | |
| HLPFILE_FreeHlpFile(page->file); | |
| } | |
| /* Local Variables: */ | |
| /* c-file-style: "GNU" */ | |
| /* End: */ |