📄 helpfile.txt
字号:
starting with lsb of Bitmap[0] is set in the Bitmap.
(Bitmap[TopicNumber>>3]&(1<<(TopicNumber&7))!=0).
*.tbl
MediaView compilers store character mapping tables listed in the [CHARTAB]
section in internal *.tbl files using the following binary structure:
struct
{
unsigned short Magic /* 0x5555 */
unsigned short Size
unsigned short Unknown1[2]
unsigned short Entries
unsigned short Ligatures
unsigned short LigLen
unsigned short Unknown2[13]
struct
{
unsigned short class
unsigned short order
unsigned char normal
unsigned char clipboard
unsigned char mac
unsigned char macclipboard
unsigned short unused
}
charentry[Entries]
unsigned char Ligature[Ligatures][LigLen]
}
CHARTAB
A character mapping table is assigned to a font by appending ,x (where x is a
decimal number) to the font name and the same ,x to the character mapping table
name (in the CHARMAP section of the internal |FONT file).
|TOPIC
And now to the interesting part, the internal file named |TOPIC. It's divided
into blocks of TopicBlockSize bytes, each beginning with a TOPICBLOCKHEADER:
TOPICPOS LastTopicLink points to last topic link in previous block or -1L
TOPICPOS FirstTopicLink points to first topic link in this block
TOPICPOS LastTopicHeader points to topic link of last topic header or 0L, -1L
----
char PlainOrCompressedData[TopicBlockSize-12]
Read the first 12 bytes into a TOPICBLOCKHEADER structure. The remaining
TopicBlockSize-12 bytes of each topic block may be compressed using the LZ77
algorithm described above.
Decompress them into a buffer of DecompressSize bytes size if the Flags value
contained in the internal |SYSTEM file is 4 or 8 and Minor is greater than 16
(DecompressSize is 16k this way), else they are not compressed and you should
copy them as delivered (DecompressSize=TopicBlockSize-12).
Do not decompress to more than DecompressSize bytes. As this would cause
ambiguos values for TOPICPOS, the help compilers will not compress more, but
fill the remaining topic block with 0es. Data will continue in the next
topic block.
TOPICPOS
A TOPICPOS is used to locate the position of TOPICLINKs in |TOPIC and contains
the TopicBlockNumber in it's higher bits and an offset into the decompression
buffer in it's lower bits.
How many bits are used for TopicBlockNumber and TopicBlockOffset depends on
the compression method used and the TopicBlockSize:
(TOPICPOS-sizeof(TOPICBLOCKHEADER))%DecompressSize = TopicBlockOffset
(TOPICPOS-sizeof(TOPICBLOCKHEADER))/DecompressSize = TopicBlockNumber
A TOPICPOS below sizeof(TOPICBLOCKHEADER) is invalid.
TOPICLINK
A TOPICLINK (located inside the buffer after decompression, the first of it
pointed to by TOPICBLOCKHEADERs FirstTopicLink field) looks like this:
long BlockSize Size of TOPICLINK + LinkData1 + compressed LinkData2
long DataLen2 length of decompressed LinkData2
TOPICPOS PrevBlock Windows 3.0 (HC30): Number of bytes previous
TOPICLINK is located before this TOPICLINC,
including eventually skipped TOPICBLOCKHEADER and
unused bytes.
Windows 3.1 (HC31): TOPICPOS of previous TOPICLINK
TOPICPOS NextBlock Windows 3.0 (HC30): Number of bytes next TOPICLINK
is located behind this TOPICLINK, incl. eventually
skipped TOPICBLOCKHEADER and unused bytes.
Windows 3.1 (HC31): TOPICPOS of next TOPICLINK
long DataLen1 includes size of TOPICLINK
unsigned char RecordType See below
----
char LinkData1[DataLen1-11]
char LinkData2[BlockSize-DataLen1]
LinkData2 may be compressed using Phrase compression. If you find
DataLen2>BlockSize-DataLen1 use the following algorithm to decompress
if your help file contains a |Phrases internal file:
Take the next character. If it's value is 0 or above 15 emit it. Else
multiply it with 256, subtract 256 and add the value of the next character.
Divide by 2 to get the phrase number. Emit the phrase from the |Phrase file
and append a space if the division had a remainder (the number was odd).
If the help file doesn't contain a |Phrases file but instead a |PhrIndex
and |PhrImage, it uses Hall compression and the decompression of LinkData2
is a bit more difficult:
Take the next character (ch). If ch is even emit the phrase number ch/2.
Else if the least two bits are 01 multiply by 64, add 64 and the value of
the next character. Emit the Phrase using this number. If the least three
bits are 011 copy the next ch/8+1 characters. If the least four bits are
0111 emit ch/16+1 spaces. If the least four bits are 1111 emit ch/16+1 NUL's.
If DataLen2<=BlockSize-DataLen1 the DataLen2 bytes of LinkData2 are stored
uncompressed (makes a difference for Hall compression only).
If DataLen2<BlockSize-DataLen1 the remaining BlockSize-DataLen1-DataLen2 bytes
are unused, but must be read from the |TOPIC file (this can only happen in Hall
compressed help files).
Now that you know how to decompress the topic data, let's see what you get.
If the TOPICLINK RecordType is 2 you got a topic header in LinkData1.
In Windows 3.0 (HC30) the TOPICHEADER is structured like this:
long BlockSize size of topic, including internal topic links
long PrevTopicNumber -1L or 0xFFFF at the beginning of a browse sequence
long NextTopicNumber -1L or 0xFFFF at the end of a browse sequence
In Windows Version 3.1 (HC31) and later it looks like this:
long BlockSize size of topic, including internal topic links
TOPICOFFSET BrowseBck topic offset for prev topic in browse sequence
TOPICOFFSET BrowseFor topic offset for next topic in browse sequence
long TopicNum topic number
TOPICPOS NonScroll start of non-scrolling region (topic offset) or -1L
TOPICPOS Scroll start of scrolling region (topic offset)
TOPICPOS NextTopic start of next type 2 record
The LinkData2 of Topic RecordType 2 contains NUL terminated strings. The
first string is the topic title, the next strings contain all macros to be
executed on opening this topic (specified using the ! footnote).
If the TOPICLINK RecordType is 1, you have a Windows 3.0 displayable text
record, a RecordType of 0x20 is Windows 3.1 displayable text and 0x23 is
a Windows 3.1 table record. A displayable text record may contain multiple
paragraphs, but all have the same paragraph formatting. A table record
stores all rows and columns of a table and may contain multiple paragraphs
of different formatting.
Data inside LinkData1 is sometimes stored as compressed shorts or longs:
A compressed unsigned short is made of a single byte. Divide by two to get
the value if it's even. Divide by two and add 128 times the value of the
next byte if it's odd.
A compressed signed short is made of a single byte. Divide by two and sub-
tract 64 to get the value if it's even. Divide by two, add 128 times the
value of the next byte and subtract 16384 if it's odd.
A compressed unsigned long is made of a 2 byte value. Divide by two to get
it's value if it's even. Divide by two and add 32768 times the value of the
next 2 bytes if it's odd.
A compressed signed long is made of a 2 byte value. Divide by two and sub-
tract 16384 to get it's value if it's even. Divide by two, add 32768 times
the value of the next 2 bytes and subtract 67108864 if it's odd.
The structure of LinkData1 in RecordType 1, 0x20, and 0x23 is difficult to
describe, as some values are only stored if a certain condition is met and
is therefore of variable size. I try to describe them as a C-structure and
note which fields are not present under certain circumstances. Don't
declare this structure. Write a parser which reads a value only if it's
condition is met.
The metric used (GapWidth, LeftIndent, etc.) is dependend upon the Font-
Descriptor used (See |FONT file). It may be HalfPoints or Twips.
compressed long TopicSize
struct only in records type 0x20 and 0x23
{
compressed unsigned short TopicLength
struct only in records type 0x23
{
unsigned char NumberOfColumns
unsigned char TableType 0,2=variable width, 1,3=normal
struct only for TableType 0 and 2
{
short MinTableWidth
}
ForTableType0or2only
struct
{
short GapWidth LeftMargin if first column
short ColWidth relative in variable width tables
Sum of all GapWidth/ColWidth values
is 32767 in variable width tables
}
Column[NumberOfColumns]
}
RecordType0x23only
}
RecordType0x20or0x23only
struct
{
struct only in RecordType 0x23
{
short column -1 if end of topic, don't continue
short unknown
char always0
}
RecordType0x23only
unsigned char unknownUnsignedChar
char unknownBiasedChar
unsigned short id
struct
{
unsigned short UnknownFollows:1
unsigned short SpacingAboveFollows:1
unsigned short SpacingBelowFollows:1
unsigned short SpacingLinesFollows:1
unsigned short LeftIndentFollows:1
unsigned short RightIndentFollows:1
unsigned short FirstlineIndentFollows:1
unsigned short unused:1
unsigned short BorderinfoFollows:1
unsigned short TabinfoFollows:1
unsigned short RightAlignedParagraph:1
unsigned short CenterAlignedParagraph:1
}
bits
compressed long Unknown only if UnknownFollows set
compressed short SpacingAbove only if SpacingAboveFollows set
compressed short SpacingBelow only if SpacingBelowFollows set
compressed short SpacingLines only if SpacingLinesFollows set
compressed short LeftIndent only if LeftIndentFollows set
compressed short RightIndent only if RightIndentFollows set
compressed short FirstlineIndent only if FirstlineIndentFollows set
struct only if BorderinfoFollows set
{
unsigned char BorderBox:1
unsigned char BorderTop:1
unsigned char BorderLeft:1
unsigned char BorderBottom:1
unsigned char BorderRight:1
unsigned char BorderThick:1
unsigned char BorderDouble:1
unsigned char BorderUnknown:1
short BorderWidth
}
Borderinfo
struct only if TabinfoFollows set
{
compressed short NumberOfTabStops
struct
{
compressed unsigned short TabStop position is lower 14 bits
struct only if TabStop bit 0x4000 set
{
compressed unsigned short TabType 1=right, 2=center
}
onlyIfTabStopBit0x4000set
}
Tab[NumberOfTabStops]
}
Tabinfo
}
Paragraphinfo
Behind this structure LinkData1 contains character formatting information.
Always output the next string (NUL terminated) from LinkData2 (use Phrase
decompression if required), than read the next formatting command, set up
the required font, color or position before displaying the next string.
Sometimes the string is of zero length, as multiple formatting commands are
required before output.
0xFF: end of character formatting. Proceed with next Paragraphinfo if
RecordType is 0x23, else you are done.
0x20: long vfldNumber 0 = {vfld} n = {vfld n}
0x21: short dtypeNumber 0 = {dtype} n = {dtype n}
0x80: short FontNumber index into Descriptor array of internal |FONT file
0x81: line break no firstlineindent/spacingabove on next paragraph
0x82: end of paragraph next paragraph has same Paragraphinfo as this one
0x83: TAB jump to next tab stop
0x86: ewc or bmc or bmcwd or bmct or button or mci
0x87: ewl or bml or bmlwd or bmlt or button or mci_left
0x88: ewr or bmr or bmrwd or bmrt or button or mci_right
unsigned char Type 5=embedded, 3 or 0x22=picture
compressed long PictureSize size of union
struct only if Type = 0x22
{
compressed word NumberOfHotspots Add to TopicPos if counting
}
OnlyIfTypeIs0x22
union
{
struct
{
short PictureIsEmbedded 0=bmc/bmr/bml or 1=bmcwd/bmlwd/bmrwd
short PictureNumber only if PictureIsEmbedded = 0
char EmbeddedPicture[PictureSize-4]
only if PictureIsEmbedded = 1
See 'Format of Pictures' section
}
Type3or0x22
struct
{
short unknown1
short unknown2
short unknown3
STRINGZ Embedded Format of string depends on statement
DLLName,WindowClass,Param if ewc/ewr/ewl
!Label,Macro if button
*n,m,[helpfilename+]filename if mci/mci_left/mci_right
n=0x8400
n+=2 if NOPLAYBAR specified
n+=8 if NOMENU specified
m=0
m+=1 if PLAY specified
n+=2 if REPEAT specified
[helpfilename+] if not EXTERNAL
}
Type5only
}
PictureData size of union is PictureSize
0x89: end of hotspot switch back from underlined green
0x8B: non-break-space the blank does not appear in LinkData2
0x8C: non-break-hyphen the hyphen itself is stored in LinkData2
0xC8: macro start with underlined green
0xCC: macro without font change
short Length
char MacroString[Length-3]
0xE0: popup jump start with underlined green
0xE1: topic jump start with underlined green
TOPICOFFSET TopicOffset
0xE2: popup jump start with underlined green
0xE3: topic jump start with underlined green
0xE3: topic jump start with underlined green
0xE6: popup jump without font change
0xE7: topic jump without font change
TOPICOFFSET TopicOffset
0xEA: popup jump into external file start with underlined green
0xEB: popup jump into external file without font change
0xEE: topic jump into external file / secondary window start with underlined green
0xEF: topic jump into external file / secondary window without font change
short SizeOfFollowingStruct
struct
{
unsigned char Type 0, 1, 4 or 6
TOPICOFFSET TopicOffset
unsigned char WindowNumber only if Type = 1
STRINGZ NameOfExternalFile only if Type = 4 or 6
STRINGZ WindowName only if Type = 6
}
Continue outputting strings from LinkData2 and parsing formatting commands
from LinkData1 until the 'end of character formatting' command is found.
TOPICOFFSET
A TOPICOFFSET is used since WinHelp 3.1 to locate a cursor-like position, even
in the middle of a topic. The position must be unique for hotspots (tabbing).
And it needs to be unique for every scrollable position (going 'Back' to a
topic that was scrolled). And it needs to quickly give you the topic block
to read from the help file.
Like a TOPICPOS, a TOPICOFFSET is divided into a TopicBlockNumber in it's
17 higher bits (TOPICPOS/32768) and a CharacterCount in it's 15 lower bits
(TOPICPOS%32768) counting all characters and the number of hotspots in
pictures appearing in all TOPICLINKs in the topic block before this position.
If you got a TopicOffset, seek to the TopicBlock in |TOPIC as told by the
TopicBlockNumber, read in and decompress the whole block. Use FirstTopicLink
to locate the first TOPICLINK in this decompressed block (CharacterCount is
0 at this place) and follow the list of TOPICLINKs up to the desired
position, adding TopicLength of every RecordType 0x20 and 0x23 you come
across, until adding TopicLength would exceed the desired CharacterPosition.
Your position is located in this TL_DISPLAY or TL_TABLE TOPICLINK. Expand
LinkData2 if phrase compressed and follow the formatting procedure described
above incrementing CharacterCount on every character (and NUL-terminator)
passed. Add the NumberOfHotspots if a picture is included.
If a TOPICLINK crosses a topic block, this has no effect on the TopicBlock-
Number for this TOPICLINK (i.e. a TOPICOFFSET pointing into the second part
has the TopicBlockNumber of the beginning of the TOPICLINK).
If you didn't come across a TOPICHEADER (TOPICLINK RecordType 2) in this
process, the beginning of the topic is located in a previous block. The
LastTopicHeader field of the TOPICBLOCKHEADER of the current block tells
you where to find it.
WALKING TOPICS
To follow all topics contained in the help file, set the current TOPICPOS
to 12 (that's FirstTopicLink of the first TOPICBLOCKHEADER at offset 0 in
|TOPIC) and load it's TopicBlock ((12-12)/DecompressSize = 0) and decompress.
The TOPICLINK is located at TopicBlockOffset ((12-12)%DecompressSize = 0)
in the decompression buffer. The first TOPICLINK contains the TOPICHEADER
of the first topic.
In Windows 3.0 (HC30) help files you move from one TOPICLINK to the next
by adding NextBlock to the current TOPICPOS. If the next TOPICLINK is
located in the next topic block, the value of NextBlock handles the jump
over the intervening TOPICBLOCKHEADER and possibly unused bytes nicely.
In Windows 3.1 (HC31) and later you move from one TOPICLINK to the next
by setting the current position to NextBlock, which also handles the jump
from one topic block to the other nicely.
The last TOPICLINK has NextBlock set to 0 or -1L. The last TOPICLINK does
not contain any usable data.
Format of Pictures
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -