winhlp
winhlp - Windows HLP file library for Python
A pure Python library for parsing Windows Help (.HLP) files. Based on helpdeco by Manfred Winterhoff, Ben Collver + Paul Wise.
winhlp.__main__
strip_raw_data
def strip_raw_data(obj)
Recursively drop ‘raw_data’ keys so the default JSON is readable.
Every parsed structure stores a raw_data blob (the source bytes plus a copy of the parsed fields), which duplicates the model 2-3x and dominates the output. –raw keeps it for byte-level fidelity; by default we remove it.
winhlp.lib.btree
Generic B+ tree implementation for HLP files.
BTreeHeader Objects
class BTreeHeader(BaseModel)
A B+ tree starts with a BTREEHEADER.
From helpdeco.h: BTREEHEADER
BTreeNodeHeader Objects
class BTreeNodeHeader(BaseModel)
B+ tree leaf-page header.
From helpdeco.h: BTREENODEHEADER
BTreeIndexHeader Objects
class BTreeIndexHeader(BaseModel)
B+ tree index-page header.
From helpdeco.h: BTREEINDEXHEADER
BTreeBuffer Objects
class BTreeBuffer(BaseModel)
State management for B+ tree iteration. Based on helpdeco’s BUFFER struct used with GetFirstPage/GetNextPage.
From helpdeco.h:
typedef struct
{
int32_t FirstLeaf;
uint16_t PageSize;
int16_t NextPage;
}
BUFFER;
BTree Objects
class BTree(BaseModel)
A B+ tree is made from leaf-pages and index-pages of fixed size, one of which is the root-page. All entries are contained in leaf-pages. If more entries are required than fit into a single leaf-page, index-pages are used to locate the leaf-page which contains the required entry.
get_first_page
def get_first_page() -> Tuple[int, BTreeBuffer]
Finds the first leaf page in the B+ tree and returns its entry count. Based on helpdeco’s GetFirstPage function.
Returns:
Tuple of (number of entries, buffer state for iteration)
get_next_page
def get_next_page(buffer: BTreeBuffer) -> int
Gets the next leaf page in the B+ tree. Based on helpdeco’s GetNextPage function.
Arguments:
buffer- State from previous get_first_page or get_next_page call
Returns:
Number of entries in the page (0 if no more pages)
iterate_leaf_pages
def iterate_leaf_pages() -> Iterator[Tuple[bytes, int]]
Iterates through all leaf pages using the GetFirstPage/GetNextPage approach.
Yields:
Tuple of (page data, number of entries)
iterate_leaf_entries_with_parser
def iterate_leaf_entries_with_parser(parse_entry_func)
Iterates through all entries in B+ tree leaf pages with a custom parser function.
This method provides a higher-level iterator that abstracts away the page-by-page iteration and header skipping, allowing callers to focus on their specific entry parsing logic.
Based on the C reference’s GetFirstPage/GetNextPage pattern but provides a cleaner, more Pythonic interface.
Arguments:
parse_entry_func- Function that takes (page_data, offset) and returns (parsed_entry, new_offset). Should return (None, offset) to skip invalid entries.
Yields:
Parsed entries from parse_entry_func (excluding None results)
winhlp.lib.hlp
Main HLP file reader class.
HLPHeader Objects
class HLPHeader(BaseModel)
A help file starts with a header, the only structure at a fixed place.
From helpdeco.h: HELPHEADER
HelpFile Objects
class HelpFile(BaseModel)
The main class for reading and parsing a HLP file.
This class represents a HLP file and provides methods to parse its contents. It loads the entire file into memory for parsing.
keyword_search_files
Maps ‘A’ -> {‘btree’: XWBTreeFile, ‘data’: XWDataFile, ‘map’: XWMapFile}
keyword_index_files
| Maps ‘A’ -> {‘btree’: XWBTreeFile, ‘data’: XWDataFile, ‘map’: XWMapFile} for | xKWBTREE files |
config_files
Maps config number -> CFnFile
grp_files
Maps filename -> GRPFile for .GRP files
chartab_files
Maps filename -> ChartabFile for .tbl files
is_gid_file
True if this is a GID file created by WinHlp32
get_topics
def get_topics() -> List[ParsedTopic]
Get all parsed topics with structured content.
get_topic_by_number
def get_topic_by_number(topic_number: int) -> Optional[ParsedTopic]
Get a specific topic by its number.
get_topic_by_context_name
def get_topic_by_context_name(context_name: str) -> Optional[ParsedTopic]
Get a topic by its context name using hash lookup.
extract_bitmap
def extract_bitmap(bitmap_name: str) -> Optional[bytes]
Extract a bitmap as BMP file data.
get_topic_with_resolved_images
def get_topic_with_resolved_images(topic_number: int) -> Optional[dict]
Get a topic with all embedded images resolved to bitmap data.
get_all_hotspots
def get_all_hotspots() -> Dict[str, List]
Get all hotspots from all bitmaps with their context names.
extract_all_text
def extract_all_text() -> str
Extract all text content as plain text.
get_topic_count
def get_topic_count() -> int
Get the total number of topics in the help file.
parse
def parse()
Parses the HLP file from the loaded data.
search_keywords
def search_keywords(char: str, keyword: str) -> List[int]
Search for topic offsets associated with a keyword.
Arguments:
char- The character identifier (A-Z, a-z) for the keyword typekeyword- The keyword to search for
Returns:
List of topic offsets where the keyword appears
get_all_keywords
def get_all_keywords(char: str) -> List[str]
Get all keywords for a specific character type.
Arguments:
char- The character identifier (A-Z, a-z) for the keyword type
Returns:
List of all keywords for the character type
get_keyword_search_statistics
def get_keyword_search_statistics() -> Dict[str, Any]
Get statistics about all keyword search files.
Returns:
Dictionary with keyword search statistics
get_config_macros
def get_config_macros(config_number: int) -> List[str]
Get all macros for a specific configuration number.
Arguments:
config_number- The configuration number (0, 1, 2, etc.)
Returns:
List of macro strings for the configuration
get_all_config_numbers
def get_all_config_numbers() -> List[int]
Get all available configuration numbers.
Returns:
List of configuration numbers that have associated files
get_config_statistics
def get_config_statistics() -> Dict[str, Any]
Get statistics about all configuration files.
Returns:
Dictionary with configuration file statistics
search_keyword_indices
def search_keyword_indices(char: str, keyword: str) -> List[int]
Search for topic offsets associated with a keyword in keyword index files.
Arguments:
char- The character identifier (A-Z, a-z) for the keyword index typekeyword- The keyword to search for
Returns:
List of topic offsets where the keyword appears
get_all_keyword_indices
def get_all_keyword_indices(char: str) -> List[str]
Get all keywords for a specific keyword index character type.
Arguments:
char- The character identifier (A-Z, a-z) for the keyword index type
Returns:
List of all keywords for the character type
get_keyword_index_statistics
def get_keyword_index_statistics() -> Dict[str, Any]
Get statistics about all keyword index files.
Returns:
Dictionary with keyword index statistics
get_macro_by_hash
def get_macro_by_hash(keyword_hash: int) -> Optional[str]
| Get a macro string by its keyword hash from the | Rose file. |
Arguments:
keyword_hash- The keyword hash to look up
Returns:
Macro string, or None if not found or no Rose file
get_all_macro_definitions
def get_all_macro_definitions() -> List[tuple]
| Get all macro definitions from the | Rose file. |
Returns:
List of (keyword_hash, macro, topic_title) tuples
find_macros_by_pattern
def find_macros_by_pattern(pattern: str) -> List[tuple]
Find macro definitions containing a pattern.
Arguments:
pattern- String pattern to search for in macro strings
Returns:
List of (keyword_hash, macro, topic_title) tuples matching the pattern
get_rose_statistics
def get_rose_statistics() -> Dict[str, Any]
Get statistics about the Rose file.
Returns:
Dictionary with Rose file statistics, or empty dict if no Rose file
get_character_mapping
def get_character_mapping(filename: str, char_code: int) -> Optional[dict]
Get character mapping information for a specific character code from a CHARTAB file.
Arguments:
filename- The CHARTAB filename (e.g., “ANSI.TBL”)char_code- The character code to look up
Returns:
Dictionary with character mapping information, or None if not found
get_all_character_mappings
def get_all_character_mappings(filename: str) -> Dict[int, dict]
Get all character mappings from a CHARTAB file.
Arguments:
filename- The CHARTAB filename (e.g., “ANSI.TBL”)
Returns:
Dictionary mapping character codes to mapping information
get_chartab_statistics
def get_chartab_statistics() -> Dict[str, Any]
Get statistics about all CHARTAB files.
Returns:
Dictionary with CHARTAB file statistics
get_available_chartab_files
def get_available_chartab_files() -> List[str]
Get list of available CHARTAB filenames.
Returns:
List of CHARTAB filenames
winhlp.lib.text_utils
Text decoding utilities for Windows Help files.
This module provides centralized text decoding functionality with encoding fallbacks to handle international character sets correctly across different Windows Help file parsers.
decode_help_text
def decode_help_text(data: bytes,
primary_encoding: Optional[str] = None) -> str
Decode byte string to text using Windows Help file appropriate encodings.
This function provides a centralized implementation of the text decoding logic that was previously duplicated across multiple parser classes (RoseFile, GMacrosFile, TopicIdFile, TTLBTreeFile, PhraseFile, etc.).
Arguments:
data- Byte data to decodeprimary_encoding- Optional primary encoding to try first (e.g., from |SYSTEM file) If None, uses cp1252 as primary
Returns:
Decoded string, with fallback handling to prevent decode errors
decode_help_text_with_system
def decode_help_text_with_system(data: bytes, system_file=None) -> str
| Decode byte string using encoding information from | SYSTEM file. |
Convenience wrapper around decode_help_text that extracts encoding from the system file if available.
Arguments:
data- Byte data to decodesystem_file- SystemFile instance with encoding information
Returns:
Decoded string
winhlp.lib.html
Render a parsed HelpFile to a single self-contained HTML document.
The whole help file becomes one HTML page: a table of contents followed by every
topic as an anchored <section>. Internal jumps/popups resolve to in-page
anchor links (via each topic’s TOPICOFFSET), character formatting comes from
the |FONT descriptors as CSS classes, and images are either embedded as data
URIs (self-contained) or written to a folder and referenced by <img src>.
HtmlExporter Objects
class HtmlExporter()
__init__
def __init__(helpfile, images: str = "embed", image_dir: Optional[str] = None)
images: “embed” (data URIs) or “extract” (write files to image_dir).
export_html
def export_html(helpfile,
images: str = "embed",
image_dir: Optional[str] = None) -> str
Render a HelpFile to a single HTML document string.
winhlp.lib.exceptions
Custom exceptions for the HLP file reader library.
HLPError Objects
class HLPError(Exception)
Base class for exceptions in this module.
InvalidHLPFileError Objects
class InvalidHLPFileError(HLPError)
Raised when the file is not a valid HLP file.
BTreeError Objects
class BTreeError(HLPError)
Raised for errors related to B-Tree parsing.
winhlp.lib.picture
Decoder for the internal lP/SHG/MRB picture format.
Pictures embedded in |bmN files and MediaView named bitmap resources are stored in the SHG/MRB “lP”/”lp” container (doc/helpfile.md:1266-1323): a magic + a table of picture offsets, each pointing at a DDB/DIB bitmap or a metafile whose dimension header is written as compressed integers and whose pixels are packed with RunLen and/or LZ77. This module decodes the first picture into a ready-to- serve Windows .bmp (bitmaps) or raw metafile (.wmf).
LP_MAGIC
“lP” (SHG) / “lp” (MRB)
decode_picture
def decode_picture(raw: bytes) -> Optional[Tuple[str, bytes]]
Decode the first picture in an lP/SHG/MRB blob into (extension, bytes).
winhlp.lib.internal_files.topicid
| Parser for the | TopicId internal file. |
TopicIdIndexEntry Objects
class TopicIdIndexEntry(BaseModel)
Structure for |TopicId index-page entries. From helpfile.md: TopicIdINDEXENTRY
TopicIdLeafEntry Objects
class TopicIdLeafEntry(BaseModel)
Structure for |TopicId leaf-page entries. From helpfile.md: TopicIdLEAFENTRY
TopicIdFile Objects
class TopicIdFile(InternalFile)
| Parses the | TopicId file, which contains context name mappings. |
From helpfile.md: The |TopicId internal file lists the ContextName assigned to a specific topic offset if the help file was created using the /a option of HCRTF and is built using a B+ tree.
Structure of |TopicId index-page entries: struct { TOPICOFFSET TopicOffset short PageNumber } TopicIdINDEXENTRY[NEntries]
Structure of |TopicId leaf-page entries: struct { TOPICOFFSET TopicOffset STRINGZ ContextName } TopicIdLEAFENTRY[NEntries]
topic_context_map
topic_offset -> context_name
context_topic_map
context_name -> topic_offset
get_context_name_for_topic
def get_context_name_for_topic(topic_offset: int) -> Optional[str]
Gets the context name for a given topic offset.
Arguments:
topic_offset- The topic offset to look up
Returns:
Context name string, or None if not found
get_topic_offset_for_context
def get_topic_offset_for_context(context_name: str) -> Optional[int]
Gets the topic offset for a given context name.
Arguments:
context_name- The context name to look up
Returns:
Topic offset, or None if not found
get_all_context_names
def get_all_context_names() -> List[str]
Returns a list of all context names in the file.
Returns:
List of context name strings
get_all_topic_offsets
def get_all_topic_offsets() -> List[int]
Returns a list of all topic offsets in the file.
Returns:
List of topic offsets
get_entry_count
def get_entry_count() -> int
Returns the total number of TopicId entries.
Returns:
Number of entries
find_contexts_by_pattern
def find_contexts_by_pattern(pattern: str) -> List[tuple]
Find context names matching a pattern (case insensitive).
Arguments:
pattern- String pattern to search for
Returns:
List of (context_name, topic_offset) tuples matching the pattern
get_statistics
def get_statistics() -> dict
Returns statistics about the TopicId data.
Returns:
Dictionary with TopicId statistics
winhlp.lib.internal_files.phrase
| Parser for the | Phrases internal file. |
PhraseFile Objects
class PhraseFile(InternalFile)
| Parses the | Phrases file, which contains phrase compression tables. |
Based on helldeco.c PhraseLoad function:
- Read PhraseCount as WORD
- Check for special VC4.0 format (PhraseCount == 0x0800)
- Read magic number (must be 0x0100)
- Read phrase offsets as WORDs
- Decompress phrase data using LZ77 (WinHelp 3.1+) or uncompressed (WinHelp 3.0)
is_new_format
VC4.0 MSDEV format
get_phrase
def get_phrase(phrase_number: int) -> Optional[str]
Gets a phrase by its number.
winhlp.lib.internal_files.petra
| Petra file parser for Windows HLP files. |
The |Petra file maps topic offsets to original RTF source filenames. It’s created when using HCRTF /a option and follows a B+ tree structure similar to |CONTEXT files.
Based on the helpdeco C reference implementation and documentation.
PetraEntry Objects
class PetraEntry(BaseModel)
A single entry in the Petra mapping table.
PetraFile Objects
class PetraFile(InternalFile)
| Parses the | Petra internal file which maps topic offsets to RTF source filenames. |
The |Petra file is created when help files are compiled with HCRTF /a option. It contains a B+ tree structure that maps TopicOffset -> RTFSourceFileName.
Structure:
- Uses B+ tree for efficient lookup
- Each leaf node contains topic offset to filename mappings
-
Similar structure to CONTEXT but with different data payload
entries
topic_offset -> rtf_filename
get_rtf_filename
def get_rtf_filename(topic_offset: int) -> Optional[str]
Get the RTF source filename for a given topic offset.
get_all_mappings
def get_all_mappings() -> Dict[int, str]
Get all topic offset to RTF filename mappings.
get_statistics
def get_statistics() -> dict
Get statistics about the Petra file.
winhlp.lib.internal_files.tomap
| Parser for the | TOMAP internal file. |
TopicPosition Objects
class TopicPosition(BaseModel)
Structure for a single topic position entry. From helpfile.md: TOPICPOS entries in |TOMAP file.
ToMapFile Objects
class ToMapFile(InternalFile)
| Parses the | TOMAP file, which contains topic position mappings for Windows 3.0 help files. |
From helpfile.md: Windows 3.0 (HC30) uses topic numbers that start at 16 for the first topic to identify topics. To retrieve the location of the TOPICLINK for the TOPIC- HEADER of a certain topic (in |TOPIC explained later), use the |TOMAP file. It contains an array of topic positions. Index with TopicNumber (do not subtract 16). TopicPos[0] points to the topic specified as INDEX in the help project.
Structure: TOPICPOS TopicPos[UsedSpace/4]
topic_positions
Array of TOPICPOS values
topic_map
topic_number -> topic_position mapping
get_topic_position
def get_topic_position(topic_number: int) -> Optional[int]
Get the topic position for a given topic number.
Arguments:
topic_number- Topic number (starts at 16 for first topic)
Returns:
Topic position or None if not found
get_index_topic_position
def get_index_topic_position() -> Optional[int]
Get the position of the INDEX topic.
From helpfile.md: TopicPos[0] points to the topic specified as INDEX in the help project.
Returns:
Position of INDEX topic or None if no topics
get_topic_count
def get_topic_count() -> int
| Get the total number of topics in the | TOMAP file. |
winhlp.lib.internal_files.context
| Parser for the | CONTEXT internal file. |
ContextIndexEntry Objects
class ContextIndexEntry(BaseModel)
Structure for |CONTEXT index-page entries.
From helpfile.md: CONTEXTINDEXENTRY
ContextLeafEntry Objects
class ContextLeafEntry(BaseModel)
Structure for |CONTEXT leaf-page entries.
From helpfile.md: CONTEXTLEAFENTRY
ContextFile Objects
class ContextFile(InternalFile)
Parses the |CONTEXT file, which contains context name hash values and their associated topic offsets. Used in WinHelp 3.1+.
From helpfile.md: Windows 3.1 (HC31) uses hash values of context names to identify topics. To get the location of the topic, search the B+ tree of the internal file |CONTEXT.
context_map
hash_value -> topic_offset
get_topic_offset_for_hash
def get_topic_offset_for_hash(hash_value: int) -> Optional[int]
Gets the topic offset for a given context name hash value.
calculate_hash
@staticmethod
def calculate_hash(context_name: str) -> int
Calculates the hash value for a context name using the algorithm from helpfile.md.
From helpfile.md: The hash value for an empty string is 1. Only 0-9, A-Z, a-z, _ and . are legal characters for context ids in Win 3.1 (HC31).
Note: The hash table contains signed byte values. Values > 0x7F are negative.
reverse_hash
@staticmethod
def reverse_hash(hash_value: int) -> str
Attempts to reverse a hash value back to a context name.
Based on the unhash() function from helpdeco.c. This generates a context ID that produces the given hash value.
derive_from_title
@staticmethod
def derive_from_title(title: str,
desired_hash: int,
win95: bool = False) -> Optional[str]
Attempts to derive a context ID from a topic title that matches the desired hash.
Based on the Derive() function from helpdeco.c. Many authoring systems create context IDs from topic titles by:
- Replacing illegal characters with _ or . or leaving them out
- Using only part of the topic title
- Prefixing with idh_ or helpid_
Arguments:
title- The topic title to derive fromdesired_hash- The hash value we’re trying to matchwin95- Whether to use Win95 character rules (allows more chars)
Returns:
A context ID that hashes to desired_hash, or None if not found
winhlp.lib.internal_files.base
Base class for internal file parsers.
InternalFile Objects
class InternalFile(BaseModel)
Base class for all internal file parsers.
winhlp.lib.internal_files.gid
Parsers for GID-specific internal files.
Based on helpfile.md documentation, GID files created by WinHlp32 contain several specific internal files that are not present in regular HLP files.
WinPosFile Objects
class WinPosFile(InternalFile)
| Parser for | WinPos internal file found in GID files. |
From helpfile.md: “This file has been seen in WinHlp32 GID files, but always contained an empty Btree (with an unknown ‘a’ in the BTREEHEADER structure).”
PeteFile Objects
class PeteFile(InternalFile)
| Parser for | Pete internal file found in GID files. |
From helpfile.md: “This file has been seen in WinHlp32 GID files but is currently not understood.”
FlagsFile Objects
class FlagsFile(InternalFile)
| Parser for | Flags internal file found in GID files. |
From helpfile.md: “This file has been seen in WinHlp32 GID files but is currently not understood.”
CntJumpFile Objects
class CntJumpFile(InternalFile)
| Parser for | CntJump internal file found in GID files. |
From helpfile.md: “This B+ tree stored in WinHlp32 GID files contains the jump references of the *.CNT file.”
CntTextFile Objects
class CntTextFile(InternalFile)
| Parser for | CntText internal file found in GID files. |
From helpfile.md: “This B+ tree stored in WinHlp32 GID files contains the topic titles of the jumps from the *.CNT file.”
winhlp.lib.internal_files.cfn
| Parser for the | CFn internal file. |
CFnFile Objects
class CFnFile(InternalFile)
| Parses the | CFn file, which contains configuration macros. |
From helpfile.md: The |CFn (where n is integer) internal file lists the macros defined in [CONFIG:n] sections of the help project file (HCW 4.00). The file contains as many macro strings as were specified one after another:
STRINGZ Macro[]
This is a simple sequential format where macros are stored as null-terminated strings one after another.
get_macros
def get_macros() -> List[str]
Returns all macros in the configuration file.
Returns:
List of macro strings
get_macro_count
def get_macro_count() -> int
Returns the number of macros in the configuration file.
Returns:
Number of macros
get_config_number
def get_config_number() -> int
Returns the configuration number extracted from the filename.
Returns:
Configuration number (0 if not determinable)
get_macro_by_index
def get_macro_by_index(index: int) -> str
Gets a macro by its index.
Arguments:
index- Zero-based index of the macro
Returns:
Macro string, or empty string if index is out of range
find_macros_by_pattern
def find_macros_by_pattern(pattern: str) -> List[tuple]
Find macros containing a pattern (case insensitive).
Arguments:
pattern- String pattern to search for
Returns:
List of (index, macro) tuples matching the pattern
get_macros_sorted
def get_macros_sorted() -> List[str]
Get all macros sorted alphabetically.
Returns:
List of macro strings sorted alphabetically
get_statistics
def get_statistics() -> dict
Returns statistics about the CFn data.
Returns:
Dictionary with CFn statistics
winhlp.lib.internal_files.phrindex
| Parser for the | PhrIndex internal file. |
PhrIndexHeader Objects
class PhrIndexHeader(BaseModel)
Header for the |PhrIndex file.
From helpdeco.h: PHRINDEXHDR
always_4a01
Sometimes 0x0001, usually 0x4A01
entries
Number of phrases
compressed_size
Size of PhrIndex file
phr_image_size
Size of decompressed PhrImage file
phr_image_compressed_size
Size of PhrImage file
always_0
Should be 0
bits
4-bit field
unknown
12-bit field
always_4a00
Sometimes 0x4A01, 0x4A02, usually 0x4A00
PhrIndexFile Objects
class PhrIndexFile(InternalFile)
| Parses the | PhrIndex file, which contains phrase compression index. |
The PhrIndex file is used for phrase compression in WinHelp 3.1+. It contains an index of phrases that can be referenced to save space in the actual help content.
From helpdeco.h PHRINDEXHDR structure:
- always4A01 (4 bytes): Magic number, sometimes 0x0001
- entries (4 bytes): Number of phrases
- compressedsize (4 bytes): Size of PhrIndex file
- phrimagesize (4 bytes): Size of decompressed PhrImage file
- phrimagecompressedsize (4 bytes): Size of PhrImage file
- always0 (4 bytes): Should be 0
- Combined 16-bit field with bits (4 bits) and unknown (12 bits)
- always4A00 (2 bytes): Magic number, sometimes 0x4A01, 0x4A02
complete_phrase_parsing
def complete_phrase_parsing(phrimage_file=None)
Complete phrase parsing after PhrImage file is available.
Arguments:
phrimage_file- The PhrImageFile object containing decompressed phrase data
winhlp.lib.internal_files.topic
| Parser for the | TOPIC internal file. |
safe_unpack_from
def safe_unpack_from(format_str: str,
data: bytes,
offset: int,
default_value=None)
Safely unpack struct data with bounds checking.
Arguments:
format_str- struct format string (e.g., “<H”, “<L”)data- bytes to unpack fromoffset- offset in data to start fromdefault_value- value to return if unpacking fails (None means raise exception)
Returns:
tuple- unpacked values or default_value on error
Raises:
struct.error- if bounds check fails and no default_value provided
safe_unpack_single
def safe_unpack_single(format_str: str,
data: bytes,
offset: int,
default_value=None)
Safely unpack a single value with bounds checking.
Arguments:
format_str- struct format string (e.g., “<H”, “<L”)data- bytes to unpack fromoffset- offset in data to start fromdefault_value- value to return if unpacking fails (None means raise exception)
Returns:
single value or default_value on error
Raises:
struct.error- if bounds check fails and no default_value provided
TopicBlockHeader Objects
class TopicBlockHeader(BaseModel)
Header for each block in the |TOPIC file.
From helpdeco.h: TOPICBLOCKHEADER
TopicLink Objects
class TopicLink(BaseModel)
A link to a topic, found within a topic block.
From helpdeco.h: TOPICLINK
TopicHeader Objects
class TopicHeader(BaseModel)
Topic header for WinHelp 3.1+ files.
From helpdeco.h: TOPICHEADER
TopicHeader30 Objects
class TopicHeader30(BaseModel)
Topic header for WinHelp 3.0 files.
From helpdeco.h: TOPICHEADER30
ParagraphInfoBits Objects
class ParagraphInfoBits(BaseModel)
Bit-packed field within ParagraphInfo.
From helpfile.md.
BorderInfo Objects
class BorderInfo(BaseModel)
Structure describing paragraph borders.
From helpfile.md.
Tab Objects
class Tab(BaseModel)
Structure for a single tab stop.
From helpfile.md.
TabInfo Objects
class TabInfo(BaseModel)
Structure for defining tab stops.
From helpfile.md.
ParagraphInfo Objects
class ParagraphInfo(BaseModel)
Variable-length structure describing paragraph formatting.
From helpfile.md.
VfldCommand Objects
class VfldCommand(BaseModel)
0x20 - {vfld n} command for MVB files
to_rtf
def to_rtf() -> str
Generate RTF output following C reference implementation.
DtypeCommand Objects
class DtypeCommand(BaseModel)
0x21 - {dtype n} command for MVB files
to_rtf
def to_rtf() -> str
Generate RTF output following C reference implementation.
FontChangeCommand Objects
class FontChangeCommand(BaseModel)
0x80 - Font change command
LineBreakCommand Objects
class LineBreakCommand(BaseModel)
0x81 - Line break command
ParagraphBreakCommand Objects
class ParagraphBreakCommand(BaseModel)
0x82 - Paragraph break command
TabCommand Objects
class TabCommand(BaseModel)
0x83 - Tab command
BitmapCommand Objects
class BitmapCommand(BaseModel)
0x86/0x87/0x88 - Bitmap commands (left/center/right aligned)
alignment
0x86=center, 0x87=left, 0x88=right
HotspotEndCommand Objects
class HotspotEndCommand(BaseModel)
0x89 - End of hotspot command
NonBreakSpaceCommand Objects
class NonBreakSpaceCommand(BaseModel)
0x8B - Non-breaking space command
NonBreakHyphenCommand Objects
class NonBreakHyphenCommand(BaseModel)
0x8C - Non-breaking hyphen command
MacroHotspotCommand Objects
class MacroHotspotCommand(BaseModel)
0xC8 - Macro hotspot command
MacroNoFontCommand Objects
class MacroNoFontCommand(BaseModel)
0xCC - Macro without font change command
PopupJumpHC30Command Objects
class PopupJumpHC30Command(BaseModel)
0xE0 - Popup jump (HC30)
TopicJumpHC30Command Objects
class TopicJumpHC30Command(BaseModel)
0xE1 - Topic jump (HC30)
PopupJumpHC31Command Objects
class PopupJumpHC31Command(BaseModel)
0xE2 - Popup jump (HC31)
TopicJumpHC31Command Objects
class TopicJumpHC31Command(BaseModel)
0xE3 - Topic jump (HC31)
PopupJumpNoFontCommand Objects
class PopupJumpNoFontCommand(BaseModel)
0xE6 - Popup jump without font change
TopicJumpNoFontCommand Objects
class TopicJumpNoFontCommand(BaseModel)
0xE7 - Topic jump without font change
ExternalPopupJumpCommand Objects
class ExternalPopupJumpCommand(BaseModel)
0xEA/0xEE - Popup jump into external file
type_field
0, 1, 4 or 6
window_number
only if Type = 1
external_file
only if Type = 4 or 6
window_name
only if Type = 6
ExternalTopicJumpCommand Objects
class ExternalTopicJumpCommand(BaseModel)
0xEB/0xEF - Topic jump into external file / secondary window
type_field
0, 1, 4 or 6
window_number
only if Type = 1
external_file
only if Type = 4 or 6
window_name
only if Type = 6
TextSpan Objects
class TextSpan(BaseModel)
A span of text with associated formatting.
TableCell Objects
class TableCell(BaseModel)
A single cell in a table with its content and formatting.
alignment
“left”, “center”, “right”
get_plain_text
def get_plain_text() -> str
Extract plain text from this cell.
TableRow Objects
class TableRow(BaseModel)
A row in a table containing multiple cells.
Table Objects
class Table(BaseModel)
A complete table structure with rows and metadata.
get_plain_text
def get_plain_text() -> str
Extract plain text representation of the table.
HotspotMapping Objects
class HotspotMapping(BaseModel)
Maps text spans to their interactive hotspot targets.
hotspot_type
“jump”, “popup”, “macro”, “external”
target
topic offset, macro command, external file, etc.
start_position
Character position in full text
ParsedTopic Objects
class ParsedTopic(BaseModel)
A fully parsed topic with structured content.
topic_offset
this topic’s TOPICOFFSET
non_scroll_offset
start of scrolling region, or None
entry_macros
macros run on entry (! footnotes)
context_names
context ids resolving to this topic
keywords
K/A keywords attached to this topic
annotations
user annotation text (from a sibling .ANN file)
browse_prev_topic
resolved browse-sequence neighbours
get_plain_text
def get_plain_text() -> str
Extract plain text content without formatting.
get_rtf_content
def get_rtf_content() -> str
Generate RTF-formatted content with rich formatting support including tables.
get_hotspots_by_type
def get_hotspots_by_type(hotspot_type: str) -> List[HotspotMapping]
Get all hotspots of a specific type (jump, popup, macro, external).
get_clickable_regions
def get_clickable_regions() -> List[dict]
Get all clickable regions with their text and targets for UI rendering.
get_hyperlinks
def get_hyperlinks() -> List[str]
Get all hyperlink targets from this topic.
get_embedded_images
def get_embedded_images() -> List[dict]
Get all embedded image references from this topic.
resolve_embedded_images
def resolve_embedded_images(hlp_file) -> List[dict]
Resolve embedded image references to actual bitmap data.
Arguments:
hlp_file- The HelpFile instance containing bitmap data
Returns:
List of dictionaries with resolved image data
TopicFile Objects
class TopicFile(InternalFile)
Parses the |TOPIC file, which holds the actual help content, including text, formatting, and links.
system_file
To be replaced with SystemFile object
topic_offset
Track TOPICOFFSET for hyperlink resolution
remaining_linkdata1
LinkData1 remaining after ParagraphInfo parsing
scan_word
@staticmethod
def scan_word(data: bytes, offset: int) -> Tuple[int, int]
Scan a compressed unsigned 16-bit integer.
From helpdec1.c: If LSB is 0: value is in one byte (shift right by 1) If LSB is 1: value is in two bytes (shift right by 1)
Returns: (value, new_offset)
scan_int
@staticmethod
def scan_int(data: bytes, offset: int) -> Tuple[int, int]
Scan a compressed signed 16-bit integer.
From helpdec1.c: If LSB is 0: value is in one byte (shift right by 1, subtract 0x40) If LSB is 1: value is in two bytes (shift right by 1, subtract 0x4000)
Returns: (value, new_offset)
scan_long
@staticmethod
def scan_long(data: bytes, offset: int) -> Tuple[int, int]
Scan a compressed 32-bit integer.
From helpdec1.c: If LSB is 0: value is in two bytes (shift right by 1, subtract 0x4000) If LSB is 1: value is in four bytes (shift right by 1, subtract 0x40000000)
Returns: (value, new_offset)
get_topic_by_number
def get_topic_by_number(topic_number: int) -> Optional[ParsedTopic]
Get a parsed topic by its topic number.
get_all_topics
def get_all_topics() -> List[ParsedTopic]
Get all parsed topics.
extract_all_text
def extract_all_text() -> str
Extract all text content from all topics as plain text.
winhlp.lib.internal_files.font
| Parser for the | FONT internal file. |
FontHeader Objects
class FontHeader(BaseModel)
Structure at the beginning of the |FONT file.
From helpdeco.h: FONTHEADER
OldFont Objects
class OldFont(BaseModel)
Font descriptor for older HLP files.
From helpdeco.h: OLDFONT
MVBFont Objects
class MVBFont(BaseModel)
Font descriptor for MultiMedia Viewer (MVP) files.
From helpdeco.h: MVBFONT
font_name
int16_t FontName
expndtw
int16_t expndtw
style
uint16_t style
fg_rgb
unsigned char FGRGB[3]
bg_rgb
unsigned char BGRGB[3]
height
int32_t Height
mostly_zero
unsigned char mostlyzero[12]
weight
int16_t Weight
unknown10
unsigned char unknown10
unknown11
unsigned char unknown11
italic
unsigned char Italic
underline
unsigned char Underline
strike_out
unsigned char StrikeOut
double_underline
unsigned char DoubleUnderline
small_caps
unsigned char SmallCaps
unknown17
unsigned char unknown17
unknown18
unsigned char unknown18
pitch_and_family
unsigned char PitchAndFamily
unknown20
unsigned char unknown20
charset
unsigned char Charset
unknown22
unsigned char unknown22
unknown23
unsigned char unknown23
unknown24
unsigned char unknown24
up
signed char up
NewFont Objects
class NewFont(BaseModel)
Font descriptor for newer HLP files.
From helpdeco.h: NEWFONT
MVBStyle Objects
class MVBStyle(BaseModel)
Character style for MultiMedia Viewer (MVP) files.
From helpdeco.h: MVBSTYLE
wStyleNum
uint16_t StyleNum
wBasedOn
uint16_t BasedOn
nf
MVBFONT font
bReserved
char unknown[35]
bStyleName
char StyleName[65]
NewStyle Objects
class NewStyle(BaseModel)
Character style for newer HLP files.
From helpdeco.h: NEWSTYLE
CharMapHeader Objects
class CharMapHeader(BaseModel)
Header for a character mapping table (*.tbl file).
From helpdeco.h: CHARMAPHEADER
CharMapEntry Objects
class CharMapEntry(BaseModel)
Entry in a character mapping table.
From helpfile.md.
FontFile Objects
class FontFile(InternalFile)
| Parses the | FONT file and manages the font descriptors. |
get_font_attributes
def get_font_attributes(font_index: Optional[int]) -> dict
Resolve a font-descriptor index into normalized character attributes.
Mirrors helpdeco’s FontLoad (helpdeco.c:2160-2234): OLDFONT stores bold/italic/underline/etc. as bits in a single Attributes byte and a HalfPoints size, whereas NEWFONT/MVBFONT store each flag in its own byte, derive bold from Weight > 500, and a size of -2 * Height. Returns {} for an out-of-range or missing index.
winhlp.lib.internal_files.xwbtree
| Parser for the | xWBTREE internal file. |
XWBTreeIndexEntry Objects
class XWBTreeIndexEntry(BaseModel)
Structure for |xWBTREE index-page entries. From helpfile.md: xWBTREEINDEXENTRY
XWBTreeLeafEntry Objects
class XWBTreeLeafEntry(BaseModel)
Structure for |xWBTREE leaf-page entries. From helpfile.md: xWBTREELEAFENTRY
XWBTreeGIDLeafEntry Objects
class XWBTreeGIDLeafEntry(BaseModel)
Structure for |xWBTREE leaf-page entries in Win95 GID files. From helpfile.md: Different structure for GID files
records
List of {‘file_number’: int, ‘topic_offset’: int}
XWBTreeFile Objects
class XWBTreeFile(InternalFile)
| Parses the | xWBTREE file, which contains keyword search index. |
From helpfile.md: To locate a keyword assigned using a x-footnote (x may be A-Z, a-z), use the |xWDATA, |xWBTREE and |xWMAP internal files. |xWBTREE tells you how often a certain Keyword is defined in the help file.
Structure of |xWBTREE index page entries: struct { STRINGZ Keyword short PageNumber } xWBTREEINDEXENTRY[NEntries]
Structure of |xWBTREE leaf page entries: struct { STRINGZ Keyword short Count number of times keyword is referenced long KWDataOffset this is the offset into |xWDATA } xWBTREELEAFENTRY[NEntries]
For Win95 GID files, the structure is different: struct { STRINGZ Keyword long Size size of following record struct { long FileNumber ? long TopicOffset this is the offset into |xWDATA } record[Size/8] } xWBTREELEAFENTRY[NEntries]
get_keyword_info
def get_keyword_info(
keyword: str
) -> Optional[Union[XWBTreeLeafEntry, XWBTreeGIDLeafEntry]]
Gets keyword information by keyword string.
Arguments:
keyword- The keyword to look up
Returns:
Keyword entry, or None if not found
get_all_keywords
def get_all_keywords() -> List[str]
Returns a list of all keywords in the file.
Returns:
List of keyword strings
get_keyword_count
def get_keyword_count() -> int
Returns the total number of keywords.
Returns:
Number of keywords
find_keywords_by_pattern
def find_keywords_by_pattern(pattern: str) -> List[str]
Find keywords matching a pattern (case insensitive).
Arguments:
pattern- String pattern to search for
Returns:
List of matching keyword strings
get_keywords_sorted
def get_keywords_sorted() -> List[str]
Get all keywords sorted alphabetically.
Returns:
List of keyword strings sorted alphabetically
get_topic_offsets_for_keyword
def get_topic_offsets_for_keyword(keyword: str) -> List[int]
Get topic offsets for a keyword (requires |xWDATA for standard format). For GID format, returns topic offsets directly.
Arguments:
keyword- The keyword to look up
Returns:
| List of topic offsets, empty if not found or requires | xWDATA |
get_statistics
def get_statistics() -> dict
Returns statistics about the xWBTREE data.
Returns:
Dictionary with xWBTREE statistics
winhlp.lib.internal_files.ctxomap
| Parser for the | CTXOMAP internal file. |
CtxoMapEntry Objects
class CtxoMapEntry(BaseModel)
Single entry in the |CTXOMAP file.
From helpdeco.h: CTXOMAPREC
CtxoMapFile Objects
class CtxoMapFile(InternalFile)
Parses the |CTXOMAP file, which contains a simple array of MapID -> TopicOffset mappings for Windows 3.0 help files.
From helpdec1.c CTXOMAPDump function:
- First 2 bytes: number of entries (uint16)
- Followed by entries, each 8 bytes:
- MapID (int32)
- TopicOffset (int32)
winhlp.lib.internal_files.ttlbtree
| Parser for the | TTLBTREE internal file. |
TTLBTreeIndexEntry Objects
class TTLBTreeIndexEntry(BaseModel)
Structure for |TTLBTREE index-page entries. From helpfile.md: TTLBTREEINDEXENTRY
TTLBTreeLeafEntry Objects
class TTLBTreeLeafEntry(BaseModel)
Structure for |TTLBTREE leaf-page entries. From helpfile.md: TTLBTREELEAFENTRY
TTLBTreeFile Objects
class TTLBTreeFile(InternalFile)
| Parses the | TTLBTREE file, which contains topic title mappings. |
From helpfile.md: If you want to know the topic title assigned using the $-footnote, take a look into the |TTLBTREE internal file, which contains topic titles ordered by topic offsets in a B+ tree. (It is used by WinHelp to display the topic titles in the search dialog).
Structure of |TTLBTREE index page entries: struct { TOPICOFFSET TopicOffset short PageNumber } TTLBTREEINDEXENTRY[NEntries]
Structure of |TTLBTREE leaf page entries: struct { TOPICOFFSET TopicOffset STRINGZ TopicTitle } TTLBTREELEAFENTRY[NEntries]
topic_title_map
topic_offset -> topic_title
title_topic_map
topic_title -> topic_offset
get_topic_title_for_offset
def get_topic_title_for_offset(topic_offset: int) -> Optional[str]
Gets the topic title for a given topic offset.
Arguments:
topic_offset- The topic offset to look up
Returns:
Topic title string, or None if not found
get_topic_offset_for_title
def get_topic_offset_for_title(topic_title: str) -> Optional[int]
Gets the topic offset for a given topic title.
Arguments:
topic_title- The topic title to look up
Returns:
Topic offset, or None if not found
get_all_topic_titles
def get_all_topic_titles() -> List[str]
Returns a list of all topic titles in the file.
Returns:
List of topic title strings
get_all_topic_offsets
def get_all_topic_offsets() -> List[int]
Returns a list of all topic offsets in the file.
Returns:
List of topic offsets
get_entry_count
def get_entry_count() -> int
Returns the total number of TTLBTREE entries.
Returns:
Number of entries
find_titles_by_pattern
def find_titles_by_pattern(pattern: str) -> List[tuple]
Find topic titles matching a pattern (case insensitive).
Arguments:
pattern- String pattern to search for
Returns:
List of (topic_title, topic_offset) tuples matching the pattern
get_titles_sorted_by_offset
def get_titles_sorted_by_offset() -> List[tuple]
Get all titles sorted by topic offset.
Returns:
List of (topic_offset, topic_title) tuples sorted by offset
get_titles_sorted_alphabetically
def get_titles_sorted_alphabetically() -> List[tuple]
Get all titles sorted alphabetically.
Returns:
List of (topic_title, topic_offset) tuples sorted by title
get_statistics
def get_statistics() -> dict
Returns statistics about the TTLBTREE data.
Returns:
Dictionary with TTLBTREE statistics
winhlp.lib.internal_files.xwdata
| Parser for the | xWDATA internal file. |
XWDataFile Objects
class XWDataFile(InternalFile)
| Parses the | xWDATA file, which contains topic offsets for keywords. |
From helpfile.md: The |xWDATA contains an array of topic offsets. The KWDataOffset from the |xWBTREE tells you where to seek to in the |xWDATA file to read Count topic offsets.
TOPICOFFSET KeywordTopicOffset[UsedSpace/4]
And the topic offset retrieved tells you which location the Keyword was assigned to. It is -1L if the Keyword is assigned to a macro using the [MACROS] section of HCRTF 4.0 (see description of |Rose file).
get_topic_offset
def get_topic_offset(index: int) -> Optional[int]
Gets a topic offset by index.
Arguments:
index- Zero-based index into the topic offset array
Returns:
Topic offset, or None if index is out of range
get_topic_offsets_range
def get_topic_offsets_range(start_offset: int, count: int) -> List[int]
Gets a range of topic offsets starting from a byte offset. This is used with KWDataOffset from |xWBTREE entries.
Arguments:
start_offset- Byte offset into the data (not index)count- Number of topic offsets to retrieve
Returns:
List of topic offsets
get_all_topic_offsets
def get_all_topic_offsets() -> List[int]
Returns all topic offsets in the file.
Returns:
List of all topic offsets
get_topic_offset_count
def get_topic_offset_count() -> int
Returns the total number of topic offsets.
Returns:
Number of topic offsets
is_macro_offset
def is_macro_offset(topic_offset: int) -> bool
Checks if a topic offset represents a macro reference.
Arguments:
topic_offset- The topic offset to check
Returns:
True if the offset is -1 (macro reference), False otherwise
get_valid_topic_offsets
def get_valid_topic_offsets() -> List[int]
Returns only valid topic offsets (excludes macro references).
Returns:
List of topic offsets that are not -1
get_macro_count
def get_macro_count() -> int
Returns the number of macro references (topic offsets that are -1).
Returns:
Number of macro references
find_offset_index
def find_offset_index(topic_offset: int) -> List[int]
Find all indices where a specific topic offset appears.
Arguments:
topic_offset- The topic offset to search for
Returns:
List of indices where the topic offset appears
get_unique_topic_offsets
def get_unique_topic_offsets() -> List[int]
Returns unique topic offsets (removes duplicates).
Returns:
List of unique topic offsets
get_statistics
def get_statistics() -> dict
Returns statistics about the xWDATA data.
Returns:
Dictionary with xWDATA statistics
winhlp.lib.internal_files.xwmap
| Parser for the | xWMAP internal file. |
XWMapEntry Objects
class XWMapEntry(BaseModel)
Structure for |xWMAP entries. From helpfile.md and helldeco.h: KWMAPREC
keyword_number
FirstRec - number of first keyword on leaf-page
page_number
PageNum - B+ tree page number
XWMapFile Objects
class XWMapFile(InternalFile)
| Parses the | xWMAP file, which contains keyword map for faster scrolling. |
From helpfile.md: The |xWMAP contains an array that tells you where to find the n-th keyword in the |xWBTREE. You don’t need to use this file but it allows for faster scrolling lists of alphabetically ordered Keywords. (WinHelp search dialog).
struct { long KeywordNumber number of first keyword on leaf-page unsigned short PageNum B+ tree page number } xWMAP[UsedSpace/6]
From helldeco.h: KWMAPREC typedef struct KWMAPREC { int32_t FirstRec; /* index number of first keyword on leaf page / uint16_t PageNum; / page number that keywords are associated with */ } KWMAPREC;
keyword_page_map
keyword_number -> page_number
get_page_for_keyword_number
def get_page_for_keyword_number(keyword_number: int) -> Optional[int]
Gets the B+ tree page number for a keyword number.
Arguments:
keyword_number- The keyword number to look up
Returns:
B+ tree page number, or None if not found
find_page_for_keyword_range
def find_page_for_keyword_range(keyword_number: int) -> Optional[int]
Finds the appropriate page for a keyword number using range lookup. This handles cases where the exact keyword number isn’t in the map.
Arguments:
keyword_number- The keyword number to find a page for
Returns:
B+ tree page number, or None if no suitable page found
get_all_entries
def get_all_entries() -> List[XWMapEntry]
Returns all xWMAP entries.
Returns:
List of all xWMAP entries
get_entry_count
def get_entry_count() -> int
Returns the total number of xWMAP entries.
Returns:
Number of entries
get_keyword_number_range
def get_keyword_number_range() -> tuple
Gets the range of keyword numbers covered by this map.
Returns:
Tuple of (min_keyword_number, max_keyword_number), or (0, 0) if empty
get_page_numbers
def get_page_numbers() -> List[int]
Gets all unique page numbers referenced in the map.
Returns:
List of unique page numbers
get_entries_for_page
def get_entries_for_page(page_number: int) -> List[XWMapEntry]
Gets all entries that reference a specific page number.
Arguments:
page_number- The page number to search for
Returns:
List of entries referencing the page
get_entries_sorted_by_keyword_number
def get_entries_sorted_by_keyword_number() -> List[XWMapEntry]
Gets all entries sorted by keyword number.
Returns:
List of entries sorted by keyword number
get_entries_sorted_by_page_number
def get_entries_sorted_by_page_number() -> List[XWMapEntry]
Gets all entries sorted by page number.
Returns:
List of entries sorted by page number
get_statistics
def get_statistics() -> dict
Returns statistics about the xWMAP data.
Returns:
Dictionary with xWMAP statistics
winhlp.lib.internal_files.catalog
| Parser for the | CATALOG internal file. |
CatalogHeader Objects
class CatalogHeader(BaseModel)
Header for the |CATALOG file.
From helpdeco.h: CATALOGHEADER
magic
Should be 0x1111
always8
Should always be 8
always4
Should always be 4
entries
Number of topic entries
zero
30 zero bytes padding
CatalogFile Objects
class CatalogFile(InternalFile)
| Parses the | CATALOG file, which contains sequential topic mapping. |
The CATALOG file maps topic numbers (1, 2, 3…) to topic offsets. This provides a simple sequential access mechanism for topics.
From helpdec1.c CatalogDump function:
- CATALOGHEADER (40 bytes): magic, always8, always4, entries, zero[30]
- Followed by entries number of 32-bit topic offsets
winhlp.lib.internal_files.gmacros
| Parser for the | GMACROS internal file. |
GMacroEntry Objects
class GMacroEntry(BaseModel)
Single macro entry in the |GMACROS file. From helpdeco.c GMACROS parsing logic.
length
Length of the record
offset
Offset of second string (exit macro)
entry_macro
Entry macro string
exit_macro
Exit macro string
GMacrosFile Objects
class GMacrosFile(InternalFile)
| Parses the | GMACROS file, which contains global macros. |
Global macros are executed when entering or exiting help contexts.
From helldeco.c parsing logic:
- First 4 bytes: count or group number
- Followed by records, each containing:
- len (4 bytes): length of record
- off (4 bytes): offset of second string in record
- First string at position 8: entry macro
- Second string at position off: exit macro
winhlp.lib.internal_files.chartab
CHARTAB parser for Windows HLP files.
CHARTAB files (*.tbl) contain character mapping tables for fonts. They are created by MediaView compilers and stored as internal files using a specific binary structure.
Based on the helpdeco C reference implementation and documentation.
ChartabHeader Objects
class ChartabHeader(BaseModel)
Header structure for CHARTAB files.
magic
Should be 0x5555
unknown_fields
Unknown fields array
ChartabCharEntry Objects
class ChartabCharEntry(BaseModel)
A character entry in the CHARTAB table.
ChartabLigature Objects
class ChartabLigature(BaseModel)
A ligature entry in the CHARTAB table.
ChartabFile Objects
class ChartabFile(InternalFile)
Parses CHARTAB (Character Mapping Table) files.
From documentation: 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 Unknown[13] } CHARTAB charentry[Entries] unsigned char Ligature[Ligatures][LigLen]
get_character_mapping
def get_character_mapping(char_code: int) -> Optional[dict]
Get character mapping information for a given character code.
get_all_mappings
def get_all_mappings() -> Dict[int, dict]
Get all character mappings.
has_ligatures
def has_ligatures() -> bool
Check if the CHARTAB file contains ligature data.
get_statistics
def get_statistics() -> dict
Get statistics about the CHARTAB file.
winhlp.lib.internal_files.grp
GRP file parser for Windows HLP files.
GRP files handle MediaView group files (.GRP) that contain group+ footnotes assigned to topics in MediaView files. These are used for organizing topics into groups with optional bitmaps.
Based on the helpdeco C reference implementation and documentation.
GroupHeader Objects
class GroupHeader(BaseModel)
Header structure for GRP files.
magic
Should be 0x000A3333
bitmap_size
max. 64000 equalling 512000 topics
last_topic
first topic in help file has topic number 0
TopicRange Objects
class TopicRange(BaseModel)
A range of topics in a group.
GRPFile Objects
class GRPFile(InternalFile)
Parses GRP (MediaView Group) files.
GRP files contain group+ footnotes assigned to topics in MediaView files. They have a specific structure with a header containing magic number 0x000A3333, bitmap size, and topic ranges.
Structure from documentation:
- Magic: 0x000A3333
- BitmapSize: max. 64000 equalling 512000 topics
- LastTopic: first topic in help file has topic number 0
- Topic ranges and group assignments
- Optional bitmap data
topic_to_group
Maps topic number to group ID
help_file
circular back-ref, not serialized
get_group_for_topic
def get_group_for_topic(topic_number: int) -> Optional[int]
Get the group ID for a given topic number.
get_topics_in_group
def get_topics_in_group(group_id: int) -> List[int]
Get all topic numbers in a given group.
get_all_groups
def get_all_groups() -> List[int]
Get all group IDs in the file.
has_bitmap
def has_bitmap() -> bool
Check if the GRP file contains bitmap data.
get_statistics
def get_statistics() -> dict
Get statistics about the GRP file.
winhlp.lib.internal_files.viola
| Parser for the | VIOLA internal file. |
ViolaEntry Objects
class ViolaEntry(BaseModel)
Single entry in the |VIOLA file.
From helpdeco.h: VIOLAREC
topic_offset
TOPICOFFSET
window_number
Window number assigned to topic
ViolaFile Objects
class ViolaFile(InternalFile)
| Parses the | VIOLA file, which contains window assignments for topics. |
The VIOLA file is structured as a B+ tree where leaf pages contain VIOLAREC entries that map topic offsets to window numbers.
From helpdeco.c:
- Uses GetFirstPage/GetNextPage to iterate through B+ tree leaf pages
- Each leaf page contains n VIOLAREC entries
- Each VIOLAREC is 8 bytes: TopicOffset (4) + WindowNumber (4)
winhlp.lib.internal_files.system
| Parser for the | SYSTEM internal file. |
SystemHeader Objects
class SystemHeader(BaseModel)
Structure at the beginning of the |SYSTEM file.
From helpdeco.h: SYSTEMHEADER
SecWindow Objects
class SecWindow(BaseModel)
Structure for a secondary window definition in the |SYSTEM file.
From helpdeco.h: SECWINDOW
KeyIndex Objects
class KeyIndex(BaseModel)
Defines a keyword index.
From helpdeco.h: KEYINDEX
DLLMaps Objects
class DLLMaps(BaseModel)
Defines mappings for 16-bit and 32-bit DLLs.
From helpfile.md.
DefFont Objects
class DefFont(BaseModel)
Default dialog font, Windows 95 (HCW 4.00)
From helpfile.md.
SystemFile Objects
class SystemFile(InternalFile)
Parses the |SYSTEM file, which contains crucial metadata about the help file’s version, compression, and configuration.
encoding
Default Windows Western European
icon
Type 5: ICON file data
cnt_filename
Type 10: CNT filename
groups
Type 13: GROUPS definitions
dllmaps
Type 19: DLLMAPS definitions
keyword_indices
Characters that have keyword index files (from type 14 records)
is_mvp
Is this a MultiMedia Viewer file?
winhlp.lib.internal_files
winhlp.lib.internal_files - Internal file parsers
Parsers for the various internal files within HLP files.
winhlp.lib.internal_files.bitmap
| Parser for bitmap files ( | bmN internal files). |
HotspotInfo Objects
class HotspotInfo(BaseModel)
Structure for hotspot information within bitmaps. From helpdeco.h: HOTSPOT
BitmapHeader Objects
class BitmapHeader(BaseModel)
Structure for bitmap file headers. Based on the C code analysis.
ExtractedBitmap Objects
class ExtractedBitmap(BaseModel)
Represents an extracted bitmap with its data and metadata.
format_type
“bmp”, “wmf”, “shg”, etc.
BitmapFile Objects
class BitmapFile(InternalFile)
| Parses bitmap files ( | bm0, | bm1, etc.) which contain images and hotspot data. |
Based on the analysis of helpdeco.c bitmap extraction code.
extract_bitmap_as_bmp
def extract_bitmap_as_bmp(bitmap_index: int = 0) -> Optional[bytes]
Extracts a bitmap as a standard Windows BMP file. Returns the complete BMP file data including headers.
extract_image
def extract_image(bitmap_index: int = 0) -> Optional[tuple]
Extract a picture as (extension, bytes), for any picture type.
Bitmaps (DDB/DIB) are wrapped as a standard .bmp; metafiles (type 8) are returned as raw .wmf metafile bytes. helpdeco writes a placeable-metafile header for WMFs, but the bare metafile record stream is the portable form and is what the decompressed picture data already contains. Returns None if the index is out of range.
get_hotspot_context_names
def get_hotspot_context_names() -> Dict[int, str]
Gets context names for all hotspots using reverse hashing. Returns a dictionary mapping hotspot hash values to context names.
winhlp.lib.internal_files.rose
| Parser for the | Rose internal file. |
RoseIndexEntry Objects
class RoseIndexEntry(BaseModel)
Structure for |Rose index-page entries. From helpfile.md: RoseINDEXENTRY
RoseLeafEntry Objects
class RoseLeafEntry(BaseModel)
Structure for |Rose leaf-page entries. From helpfile.md: RoseLEAFENTRY
topic_title
Display string for search dialog
RoseFile Objects
class RoseFile(InternalFile)
| Parses the | Rose file, which contains macro definitions from [MACROS] section. |
From helpfile.md: The |Rose internal file contains all definitions from the [MACROS] section of a Windows 95 (HCW 4.00) help project file. It is built using a B+ tree. Keywords only appear using hash values but are listed in the |KWBTREE with a TopicPos in the associated |KWDATA array of -1L.
Structure of |Rose index page entries: struct { long KeywordHash short PageNumber } RoseINDEXENTRY[NEntries]
Structure of |Rose leaf page entries: struct { long KeywordHash STRINGZ Macro STRINGZ TopicTitle not a real topic title but the string displayed in the search dialog where normally topic titles are listed } RoseLEAFENTRY[NEntries]
macro_map
keyword_hash -> RoseLeafEntry
get_macro_by_hash
def get_macro_by_hash(keyword_hash: int) -> Optional[RoseLeafEntry]
Gets a macro entry by its keyword hash.
Arguments:
keyword_hash- The keyword hash to look up
Returns:
Rose entry, or None if not found
get_macro_string_by_hash
def get_macro_string_by_hash(keyword_hash: int) -> Optional[str]
Gets just the macro string by its keyword hash.
Arguments:
keyword_hash- The keyword hash to look up
Returns:
Macro string, or None if not found
get_all_keyword_hashes
def get_all_keyword_hashes() -> List[int]
Returns a list of all keyword hashes in the file.
Returns:
List of keyword hash values
get_all_macros
def get_all_macros() -> List[str]
Returns a list of all macro strings in the file.
Returns:
List of macro strings
get_all_entries
def get_all_entries() -> List[RoseLeafEntry]
Returns all Rose entries.
Returns:
List of all Rose entries
get_entry_count
def get_entry_count() -> int
Returns the total number of Rose entries.
Returns:
Number of entries
find_macros_by_pattern
def find_macros_by_pattern(pattern: str) -> List[RoseLeafEntry]
Find macro entries where the macro string contains a pattern (case insensitive).
Arguments:
pattern- String pattern to search for
Returns:
List of Rose entries matching the pattern
find_by_topic_title_pattern
def find_by_topic_title_pattern(pattern: str) -> List[RoseLeafEntry]
Find macro entries where the topic title contains a pattern (case insensitive).
Arguments:
pattern- String pattern to search for
Returns:
List of Rose entries matching the pattern
get_entries_sorted_by_hash
def get_entries_sorted_by_hash() -> List[RoseLeafEntry]
Get all entries sorted by keyword hash.
Returns:
List of entries sorted by keyword hash
get_entries_sorted_by_macro
def get_entries_sorted_by_macro() -> List[RoseLeafEntry]
Get all entries sorted alphabetically by macro string.
Returns:
List of entries sorted by macro string
get_entries_sorted_by_topic_title
def get_entries_sorted_by_topic_title() -> List[RoseLeafEntry]
Get all entries sorted alphabetically by topic title.
Returns:
List of entries sorted by topic title
get_statistics
def get_statistics() -> dict
Returns statistics about the Rose data.
Returns:
Dictionary with Rose statistics
winhlp.lib.internal_files.phrimage
| Parser for the | PhrImage internal file. |
PhrImageFile Objects
class PhrImageFile(InternalFile)
| Parses the | PhrImage file, which contains phrase strings for Hall compression. |
Based on helldeco.c PhraseLoad function: The |PhrImage file stores the actual phrase strings used in Hall compression. It works with |PhrIndex to provide phrase compression in Windows 95 help files.
From helpfile.md: The |PhrImage file stores the phrases. A phrase is not NUL-terminated. Use PhraseOffset[NumPhrase] and PhraseOffset[NumPhrase+1] to locate beginning and end of the phrase string. |PhrImage is LZ77 compressed if PhrImageCompressedSize is not equal to PhrImageSize.
get_phrase
def get_phrase(phrase_number: int) -> Optional[str]
Gets a phrase by its number.
Arguments:
phrase_number- Zero-based phrase index
Returns:
The phrase string, or None if not found
get_phrase_count
def get_phrase_count() -> int
Returns the total number of phrases stored.
get_raw_phrase_data
def get_raw_phrase_data(start_offset: int, end_offset: int) -> bytes
Gets raw phrase data between two offsets.
Arguments:
start_offset- Starting byte offset in decompressed dataend_offset- Ending byte offset in decompressed data
Returns:
Raw phrase bytes, or empty bytes if invalid range
decode_phrase_bytes
def decode_phrase_bytes(phrase_bytes: bytes) -> str
Decode phrase bytes to string using appropriate encoding.
Arguments:
phrase_bytes- Raw phrase bytes
Returns:
Decoded phrase string
get_statistics
def get_statistics() -> dict
Returns statistics about the phrase data.
Returns:
Dictionary with phrase statistics
winhlp.lib.ann
Parser for Windows Help Annotation (.ANN) files.
Based on helpfile.md documentation: “An annotation file created by WinHelp uses the same basic file format as a Windows help file. The first 16 bytes contain the same header as a help file, with same Magic.”
ANN files contain user annotations for help topics and follow this structure:
- @VERSION: Contains version info (0x08 0x62 0x6D 0x66 0x01 0x00)
- @LINK: Contains number of annotations and TOPICOFFSET for each annotation
- n!0: Individual annotation text files (e.g., “12345!0”) containing plain ANSI text
AnnotationReference Objects
class AnnotationReference(BaseModel)
Reference to an annotation in the @LINK file.
unknown1
always 0 according to docs
unknown2
always 0 according to docs
VersionFile Objects
class VersionFile(InternalFile)
Parser for @VERSION internal file in ANN files.
LinkFile Objects
class LinkFile(InternalFile)
Parser for @LINK internal file in ANN files.
AnnotationTextFile Objects
class AnnotationTextFile(InternalFile)
Parser for individual annotation text files (e.g., “12345!0”).
AnnotationFile Objects
class AnnotationFile()
Parser for Windows Help Annotation (.ANN) files.
ANN files use the same basic file format as Windows help files but contain user annotations for help topics instead of help content.
__init__
def __init__(filepath: str)
Initialize ANN file parser.
Arguments:
filepath- Path to the .ANN annotation file
get_annotations
def get_annotations() -> List[Dict]
Get all annotations with their topic offsets and text.
Returns:
List of annotation dictionaries with ‘topic_offset’ and ‘text’ keys
get_annotation_for_topic
def get_annotation_for_topic(topic_offset: int) -> Optional[str]
Get annotation text for a specific topic offset.
Arguments:
topic_offset- The topic offset to look up
Returns:
Annotation text or None if not found
get_statistics
def get_statistics() -> Dict
Get statistics about the annotation file.
Returns:
Dictionary with annotation file statistics
winhlp.lib.compression
Decompression algorithms for HLP files.
lz77_decompress
def lz77_decompress(data: bytes) -> bytes
Decompresses LZ77 compressed data from a HLP file.
Optimized implementation based on helldec1.c from helpdeco. Uses circular buffer and efficient bit processing like the C version.
phrase_decompress
def phrase_decompress(data: bytes, phrases: List[str]) -> bytes
Decompresses phrase-compressed data.
From helpfile.md: 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).
hall_decompress
def hall_decompress(data: bytes, phrases: List[str]) -> bytes
Decompresses Hall-compressed data (Windows 95 HCW 4.00).
From helpfile.md: 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.
runlen_decompress
def runlen_decompress(data: bytes) -> bytes
Decompresses run-length compressed data.
Based on helpdeco’s DeRun function. The algorithm works with a global count variable that tracks run-length state:
- If count & 0x7F is non-zero, we’re in a run
- If count & 0x80 is set, emit characters one by one
- Otherwise emit the full run at once
- When count reaches 0, read next signed byte as new count
From C code: signed char count; /* for run len decompression */ count = (signed char)c;
decompress
def decompress(method: int, data: bytes, phrases: List[str] = None) -> bytes
Decompresses data using the specified method, matching helpdeco’s approach.
Method values:
- 0: copy (no decompression)
- 1: runlen decompression
- 2: LZ77 decompression
- 3: runlen and LZ77 decompression
winhlp.lib.directory
Parses the internal file directory of a HLP file.
FileHeader Objects
class FileHeader(BaseModel)
Structure at the start of each internal file.
From helpdeco.h: FILEHEADER
DirectoryLeafEntry Objects
class DirectoryLeafEntry(BaseModel)
The structure of directory leaf-pages.
From helpfile.md:
struct
{
STRINGZ FileName varying length NUL-terminated string
long FileOffset offset of FILEHEADER of internal file FileName
relative to beginning of help file
}
DIRECTORYLEAFENTRY[NEntries]
Directory Objects
class Directory(BaseModel)
The internal directory which is used to associate FileNames and FileOffsets. The directory is structured as a B+ tree.
winhlp.lib
winhlp.lib - Core library components
Internal modules for parsing HLP file structures.