Skip to content

feat: zlib preset dictionary decompressor for S7CommPlus blobs#776

Open
gijzelaerr wants to merge 6 commits into
masterfrom
feat/zlib-preset-dictionaries
Open

feat: zlib preset dictionary decompressor for S7CommPlus blobs#776
gijzelaerr wants to merge 6 commits into
masterfrom
feat/zlib-preset-dictionaries

Conversation

@gijzelaerr

Copy link
Copy Markdown
Owner

Summary

The key dictionary for #757 is IntfDescTag (Adler-32 0xce9b821b, 1178 bytes) — used for I/Q/M area tag definitions on V3 PLCs (S7-1200 FW V4.5+). With the complete dictionary, decompression produces clean XML that can be parsed with standard tools instead of regex extraction from garbled output.

Addresses #757.

Test plan

  • Verify all 19 dictionary Adler-32 checksums match
  • Decompress standard (no-dict) and preset-dict blobs
  • Unknown dictionary raises clear error
  • find_and_decompress() scans raw payloads for zlib streams
  • @tommasofaedo: test against S7-1200 FW V4.5 I/Q/M EXPLORE responses

🤖 Generated with Claude Code

gijzelaerr and others added 3 commits July 16, 2026 21:25
)

S7-1200/1500 PLCs compress XML metadata (tag definitions, interface
descriptions, network comments, etc.) using zlib with Siemens-specific
preset dictionaries. Python's zlib.decompress() returns Z_NEED_DICT for
these blobs. This adds the 19 known dictionaries and a decompressor
that auto-selects the correct one by Adler-32 checksum.

The dictionaries were extracted from thomas-v2/S7CommPlusDriver
(LGPL-3.0, BlobDecompressor.cs). Key dictionary for #757:
IntfDescTag (0xce9b821b) — used for I/Q/M area tag definitions on
V3 PLCs (S7-1200 FW V4.5+).

New modules:
- s7commplus/zlib_dicts.py — 19 preset dictionaries (24KB total)
- s7commplus/blob_decompressor.py — decompress_blob() with auto
  dictionary lookup, find_and_decompress() for scanning raw payloads

Replaces the partial oracle-based dictionary reconstruction in
examples/browse_tags.py with complete, verified dictionaries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dictionaries are 100% printable XML template fragments (with the
occasional German ü). Storing them as hex blobs made them unreadable.
Now stored as Python string literals, encoded to bytes at import time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the 385-line Python module of string-chunked XML with 19
individual .xml files in s7commplus/zlib_dicts/. Each file is named
{adler32}_{name}.xml and can be read directly in any editor or IDE.

The __init__.py loader scans the directory at import time and builds
the same ZLIB_DICTIONARIES/ZLIB_DICT_NAMES dicts as before.

Also adds *.xml to pyproject.toml package-data so the files ship
in the wheel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gijzelaerr and others added 3 commits July 16, 2026 21:55
The end-of-file-fixer hook requires a trailing newline. The loader
strips it with rstrip(b"\n") so the Adler-32 checksums stay correct.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The .xml dictionary files contain raw zlib preset dictionary bytes, not
valid standalone XML. Mark them as binary in .gitattributes to prevent
CRLF conversion on Windows (which broke Adler-32 checksums), and exclude
them from the check-xml pre-commit hook.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add explore_xml() to both sync and async clients — sends an EXPLORE
request and decompresses the zlib-compressed XML metadata from the
response. Also export decompress_blob and find_and_decompress from the
s7commplus package for direct use with raw EXPLORE payloads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant