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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

Arguments:

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:

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:

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:

complete_phrase_parsing

def complete_phrase_parsing(phrimage_file=None)

Complete phrase parsing after PhrImage file is available.

Arguments:

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:

Returns:

Raises:

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:

Returns:

single value or default_value on error

Raises:

TopicBlockHeader Objects

class TopicBlockHeader(BaseModel)

Header for each block in the |TOPIC file. From helpdeco.h: TOPICBLOCKHEADER

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.

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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.