993 lines
23 KiB
C
993 lines
23 KiB
C
/*
|
|
* 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: */
|