Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Zlib preset dictionaries: exact bytes matter for Adler-32 checksums
s7commplus/zlib_dicts/*.xml binary
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repos:
- id: check-symlinks
- id: check-toml
- id: check-xml
exclude: ^s7commplus/zlib_dicts/
- id: check-yaml
- id: check-illegal-windows-names
- id: check-merge-conflict
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ discovery = ["pnio-dcp"]

[tool.setuptools.package-data]
snap7 = ["py.typed"]
s7commplus = ["py.typed"]
s7commplus = ["py.typed", "zlib_dicts/*.xml"]

[tool.setuptools.packages.find]
include = ["snap7*", "s7*", "s7commplus*"]
Expand Down
3 changes: 3 additions & 0 deletions s7commplus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
data = client.db_read(1, 0, 4)
"""

from .blob_decompressor import decompress_blob, find_and_decompress
from .client import S7CommPlusClient as Client
from .async_client import S7CommPlusAsyncClient as AsyncClient
from .server import S7CommPlusServer as Server, DataBlock, CPUState
Expand All @@ -25,4 +26,6 @@
"DataBlock",
"CPUState",
"S7CommPlusConnection",
"decompress_blob",
"find_and_decompress",
]
21 changes: 21 additions & 0 deletions s7commplus/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
parse_server_session_version,
)
from .vlq import encode_uint32_vlq, decode_uint32_vlq, decode_uint64_vlq
from .blob_decompressor import find_and_decompress
from .client import (
_build_read_payload,
_parse_read_response,
Expand Down Expand Up @@ -478,6 +479,26 @@ async def explore(self, explore_id: int = 0) -> bytes:
payload = _build_explore_payload(explore_id)
return await self._send_request(FunctionCode.EXPLORE, payload)

async def explore_xml(self, explore_id: int = 0) -> str | None:
"""EXPLORE a PLC object and decompress the XML metadata from the response.

S7-1200/1500 PLCs (FW V4.5+) compress XML metadata — tag definitions,
interface descriptions, line comments, etc. — using zlib with Siemens
preset dictionaries. This method sends an EXPLORE request and
decompresses the first zlib stream found in the response.

.. warning:: This method is **experimental** and may change.

Args:
explore_id: Object to explore (0 = root).

Returns:
Decompressed XML as a UTF-8 string, or ``None`` if the response
contains no recognisable zlib stream.
"""
raw = await self.explore(explore_id)
return find_and_decompress(raw)

async def set_plc_operating_state(self, state: int) -> None:
"""Set the PLC operating state (start/stop)."""
payload = _build_invoke_payload(state)
Expand Down
89 changes: 89 additions & 0 deletions s7commplus/blob_decompressor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Decompress zlib-compressed blobs from S7-1200/1500 PLCs.

S7CommPlus PLCs compress XML metadata (tag definitions, network comments,
interface descriptions, etc.) using zlib with Siemens-specific preset
dictionaries. Python's ``zlib.decompress()`` returns ``Z_NEED_DICT`` for
these blobs; this module supplies the matching dictionary.

The 19 preset dictionaries were extracted from thomas-v2/S7CommPlusDriver
(LGPL-3.0) and are stored in ``zlib_dicts.py``.

Usage::

data = decompress_blob(compressed_bytes)
# data is the decompressed XML string (UTF-8)
"""

import logging
import zlib

from .zlib_dicts import ZLIB_DICTIONARIES, ZLIB_DICT_NAMES

logger = logging.getLogger(__name__)


def decompress_blob(data: bytes, offset: int = 0) -> str:
"""Decompress a zlib blob, auto-supplying the preset dictionary if needed.

Args:
data: Raw bytes containing the zlib stream.
offset: Starting position within ``data``. Some blobs have a
4-byte version prefix before the zlib header.

Returns:
Decompressed content as a UTF-8 string.

Raises:
ValueError: If the zlib stream requires an unknown dictionary.
zlib.error: On other decompression failures.
"""
stream = data[offset:]

# Check for preset-dictionary zlib header: CMF=0x78, FLG with FDICT bit set (bit 5)
if len(stream) >= 6 and stream[0] == 0x78 and (stream[1] & 0x20):
dict_adler = int.from_bytes(stream[2:6], "big")
zdict = ZLIB_DICTIONARIES.get(dict_adler)
else:
# Standard zlib stream — no preset dictionary
return zlib.decompress(stream).decode("utf-8")

# Preset dictionary required

if zdict is None:
name = ZLIB_DICT_NAMES.get(dict_adler, "unknown")
raise ValueError(
f"Unknown zlib preset dictionary: adler32=0x{dict_adler:08x} ({name}). "
f"Known dictionaries: {', '.join(ZLIB_DICT_NAMES.values())}"
)

logger.debug("Using preset dictionary: %s (0x%08x)", ZLIB_DICT_NAMES.get(dict_adler, "?"), dict_adler)

dobj = zlib.decompressobj(wbits=-15, zdict=zdict)
# Skip the 6-byte zlib header (2 magic + 4 dict adler)
result = dobj.decompress(stream[6:])
return result.decode("utf-8")


def find_and_decompress(data: bytes) -> str | None:
"""Scan raw bytes for a zlib stream with preset dictionary and decompress it.

Searches for the ``78 7D`` magic (zlib level 6 + FDICT flag) that
indicates a preset-dictionary stream. Falls back to ``78 01``/``78 5E``/
``78 9C``/``78 DA`` for standard streams.

Args:
data: Raw EXPLORE response payload (possibly multi-fragment).

Returns:
Decompressed UTF-8 string, or ``None`` if no zlib stream found.
"""
# Preset-dictionary magic: CMF=0x78 (deflate, window=32K), FLG=0x7D (FDICT set)
for magic in (b"\x78\x7d", b"\x78\x01", b"\x78\x5e", b"\x78\x9c", b"\x78\xda"):
pos = data.find(magic)
if pos >= 0:
try:
return decompress_blob(data, offset=pos)
except (ValueError, zlib.error) as e:
logger.debug("Decompression failed at offset %d: %s", pos, e)
continue
return None
21 changes: 21 additions & 0 deletions s7commplus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Any, Optional

from . import typeinfo
from .blob_decompressor import find_and_decompress
from .connection import S7CommPlusConnection
from .protocol import FunctionCode, Ids, ElementID, DataType, ObjectId
from .vlq import encode_uint32_vlq, decode_uint32_vlq, decode_uint64_vlq
Expand Down Expand Up @@ -284,6 +285,26 @@ def explore(self, explore_id: int = 0) -> bytes:
response = self._connection.send_request(FunctionCode.EXPLORE, payload, integrity_tail=5, reassemble=True)
return response

def explore_xml(self, explore_id: int = 0) -> str | None:
"""EXPLORE a PLC object and decompress the XML metadata from the response.

S7-1200/1500 PLCs (FW V4.5+) compress XML metadata — tag definitions,
interface descriptions, line comments, etc. — using zlib with Siemens
preset dictionaries. This method sends an EXPLORE request and
decompresses the first zlib stream found in the response.

.. warning:: This method is **experimental** and may change.

Args:
explore_id: Object to explore (0 = root).

Returns:
Decompressed XML as a UTF-8 string, or ``None`` if the response
contains no recognisable zlib stream.
"""
raw = self.explore(explore_id)
return find_and_decompress(raw)

def set_plc_operating_state(self, state: int) -> None:
"""Set the PLC operating state (start/stop).

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Attribut Key=Value="" />falsetrueChecksFlagsCompilerTypeOptimizationFlagsTargetTypeMC7PlusIECShortWires_x002C__x0020_NativePointerEnableAllCheck_Classic<Struct Key="</Struct><Language Key="</Language><Target></Target><CompilerSettingsDocument Version="</CompilerSettingsDocument>FwVersionLanguageLevelMlfbPlcFamilyCalleeRenumberingPossibleCommonFBD_CLASSICFBD_IECLAD_CLASSICLAD_IECSTLCreateExtendedDebugMonitorArrayLimitsSetFlagAutomatically
1 change: 1 addition & 0 deletions s7commplus/zlib_dicts/1bac39f0_DebugInfo_90000001.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<BlockDebugInfo Version="10.5.18.4" TimeStamp="128680924506527258"><Target Type="MC7Plus"><Net Id="0" EndAddress="60" EndAddressXml="-1" EndStatementIndex="31" /> </Target></BlockDebugInfo>
1 change: 1 addition & 0 deletions s7commplus/zlib_dicts/3c55436a_LineComm_98000001.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UId=" RefID="<Part>51:52:53:54:55<BodyLineComments><CommentDictionary><InterfaceLineComments<Part Version="2.0" ID=" Kind=" ParentID="<CommentCommentID=" Path=" fr-FRit-IT<DictEntry Language="de-DE"en-US=">
1 change: 1 addition & 0 deletions s7commplus/zlib_dicts/4b8416f0_IntfDesc_90000001.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-16"?><BlockInterface Version="1.0"> <Source Version="1.0"><Section Name="Static"> <Line LId="9" Name="START" Symbol="Time" Library.Size="32" Library.Layout.ByteOffset="0" Remanence="Volatile" RId="0x200000b" /> <Line LId="10" Name="PRESET" Symbol="Time" Library.Size="32" Library.Layout.ByteOffset="4" Remanence="Volatile" RId="0x200000b" /> <Line LId="11" Name="ELAPSED" Symbol="Time" Library.Size="32" Library.Layout.ByteOffset="8" Remanence="Volatile" RId="0x200000b" /> <Line LId="12" Name="RUNNING" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="12.0" Remanence="Volatile" RId="0x2000001" /> <Line LId="13" Name="IN" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="12.1" Remanence="Volatile" RId="0x2000001" /> <Line LId="14" Name="Q" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="12.2" Remanence="Volatile" RId="0x2000001" /> <Line LId="15" Name="PAD" Symbol="Byte" Library.Size="8" Library.Layout.ByteOffset="13" Remanence="Volatile" RId="0x2000002" /> <Line LId="16" Name="PAD_1" Symbol="Byte" Library.Size="8" Library.Layout.ByteOffset="14" Remanence="Volatile" RId="0x2000002" /> <Line LId="17" Name="PAD_2" Symbol="Byte" Library.Size="8" Library.Layout.ByteOffset="15" Remanence="Volatile" RId="0x2000002" /></Section> </Source> <StartValues><dictionary_entries></dictionary_entries> </StartValues></BlockInterface><?xml version="1.0" encoding="utf-16"?><BlockInterface Version="1.0"> <Source Version="1.0" NextFreeLId="10" ClassicSize="0" RetainSize="0" VolatileSize="0" Library.LStackBitsize="0"><Section Name="Input" /><Section Name="Output" /><Section Name="InOut" /><Section Name="Temp" /><Section Name="Return"> <Line Name="Ret_Val" Symbol="Void" LId="9" Accessibility="Public" Remanence="Volatile" Library.Size="0" Library.Layout.ByteOffset="0" RId="0x2000000" /></Section> </Source></BlockInterface><?xml version="1.0" encoding="utf-16"?><BlockInterface Version="1.0"> <Source Version="1.0" System="True" ClassicSize="0" RetainSize="0" VolatileSize="0" NextFreeLId="11" Library.LStackBitsize="0"><Section Name="Temp"> <Line LId="9" Name="first_scan" Accessibility="Public" Remanence="Volatile" Symbol="Bool" RId="0x2000001" /> <Line LId="10" Name="remanence" Accessibility="Public" Remanence="Volatile" Symbol="Bool" RId="0x2000001" /></Section> </Source></BlockInterface><?xml version="1.0" encoding="utf-16"?><BlockInterface Version="1.0"> <Source Version="1.0" ClassicSize="2768" RetainSize="0" VolatileSize="32" NextFreeLId="128" Library.LStackBitsize="0"><Section Name="Static"> <Line LId="9" Name="stat_1" Accessibility="Public" Remanence="Volatile" Symbol="Int" Library.Size="16" Library.Layout.ByteOffset="0" Initial="12" RId="0x2000005" /> <Line LId="10" Name="stat_2" Accessibility="Public" Remanence="Volatile" Symbol="Real" Library.Size="32" Library.Layout.ByteOffset="2" Initial="1.5" RId="0x2000008" /> <Line LId="11" Name="stat_3" Accessibility="Public" Remanence="Volatile" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="6.0" Initial="" RId="0x2000001" /> <Line LId="15" Name="stat_4" Accessibility="Public" Remanence="Volatile" Symbol="Time" Library.Size="32" Library.Layout.ByteOffset="18" RId="0x200000b" /> <Line LId="19" Name="stat_5" Accessibility="Public" Remanence="Volatile" Symbol="Word" Library.Size="16" Library.Layout.ByteOffset="30" Initial="12" RId="0x2000004" /></Section> </Source> <StartValues><dictionary_entries></dictionary_entries> </StartValues></BlockInterface><?xml version="1.0" encoding="utf-16"?><BlockInterface Version="1.0"> <Source Version="1.0"><Section Name="Static"> <Line LId="9" Name="COUNT_UP" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="0.0" Remanence="Volatile" RId="0x2000001" /> <Line LId="10" Name="COUNT_DOWN" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="0.1" Remanence="Volatile" RId="0x2000001" /> <Line LId="11" Name="RESET" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="0.2" Remanence="Volatile" RId="0x2000001" /> <Line LId="12" Name="LOAD" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="0.3" Remanence="Volatile" RId="0x2000001" /> <Line LId="13" Name="Q_UP" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="0.4" Remanence="Volatile" RId="0x2000001" /> <Line LId="14" Name="Q_DOWN" Symbol="Bool" Library.Size="1" Library.Layout.BitOffset="0.5" Remanence="Volatile" RId="0x2000001" /> <Line LId="15" Name="PAD" Symbol="Byte" Library.Size="8" Library.Layout.ByteOffset="1" Remanence="Volatile" RId="0x2000002" /> <Line LId="16" Name="PRESET_VALUE" Symbol="Int" Library.Size="16" Library.Layout.ByteOffset="2" Remanence="Volatile" RId="0x2000005" /> <Line LId="17" Name="COUNT_VALUE" Symbol="Int" Library.Size="16" Library.Layout.ByteOffset="4" Remanence="Volatile" RId="0x2000005" /></Section> </Source> <StartValues><dictionary_entries></dictionary_entries> </StartValues></BlockInterface>
1 change: 1 addition & 0 deletions s7commplus/zlib_dicts/5814b03b_IdentES_98000001.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<IdentES version="1.0"><CoreSubtype>FC</CoreSubtype><ObjectTypeInfo>CodeBlockData</ObjectTypeInfo><CompileTime>633706353564000671</CompileTime><OnlySymbolicAccess>False</OnlySymbolicAccess><HeaderData Version="0.1" /><IsSystem>true</IsSystem></IdentES><IdentES version="1.0"><CoreSubtype>DB</CoreSubtype><ObjectTypeInfo>DataBlockData</ObjectTypeInfo><CompileTime>633706353581345975</CompileTime><DatablockType type="SharedDB" oftype="Undef" ofnumber="0" /><OnlySymbolicAccess>False</OnlySymbolicAccess><HeaderData Version="0.1" /><IsSystem>true</IsSystem></IdentES><IdentES version="1.0"><CoreSubtype>DB</CoreSubtype><ObjectTypeInfo>DataBlockData</ObjectTypeInfo><CompileTime>633706353587127743</CompileTime><DatablockType type="IDBofSDT" oftype="SDT" ofnumber="0" ofname="IEC_COUNTER" ofTypeTypeGuid="7e93fc34-5398-48b1-a317-38b8cd37b8e8" ofTypeVersionGuid="007ed7c6-51bd-405c-b7e5-e1fd3f25a6c0" /><OnlySymbolicAccess>True</OnlySymbolicAccess><HeaderData Version="0.1" /><IsSystem>false</IsSystem></IdentES><IdentES version="1.0"><CoreSubtype>OB.ProgramCycle</CoreSubtype><ObjectTypeInfo>CodeBlockData</ObjectTypeInfo><CompileTime>633706354424546519</CompileTime><OnlySymbolicAccess>False</OnlySymbolicAccess><HeaderData Version="0.1" /><IsSystem>true</IsSystem></IdentES>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Ident><Base<FBT ReferencedTypesIndex=" ClassicSlot=" VolatileSlot=" RetainSlot="<CompileUnitIdent><MfbUDT IdentRefID=" RetainParameter=" VolatileParameter=" ClassicParameter="<Multiinstance<IdentContainer><Ident<CrossRefInfo><Access> BlockNumber=" BlockType=" TypeName=" StructureModifiedTS=" InterfaceModifiedTS=" TypeObjectId=" VersionId=" TypeRId=" RId=" MIRetainPaddedBitSize=" MIVolatilePaddedBitSize=" MIClassicPaddedBitSize=" MIRetainRelativeBitOffset=" MIVolatileRelativeBitOffset=" MIClassicRelativeBitOffset=" Scope=" RefId="<XRefItem<Block UId=" Usage=" Instruction=" NetId=" XRefHidden=" SlotNumber="<ExternalTypes<Datatype ClassicBitsize=" RetainBitsize=" VolatileBitsize="<ExternalType<IdentStorage><Part> Multiinstance_StartOfRetainPart=" Multiinstance_StartOfVolatilePart=" Multiinstance_StartOfClassicPart="<Values WidestMemberBitsize=" PaddedElementBitsize=" IdentRefId=" OperandType=" OffsetModifiedTimestamp=" OffsetCompileTimestamp="<CallStackUsage UseAnnotatedOffsets="<?xml version="1.0" encoding="utf-16"?><BlockInterface<Usage LibReference="xmlns="http://schemas.siemens.com/Simatic/ES/11/BlockInterface/Source/V11_01.xsd TypeInfoRuntimeId="<Root<OffsetData XmlPartID=" BlockTypeFamily=" BitSlotCount=" Slot8Count=" Slot16Count=" Slot32Count=" Slot64Count=" SlotSingleDoubleCount=" SlotPointerCount="<SubParts> RIdSlots=" IdentUID=" Parameter=" InterfaceGuid=" BlockObjectID=" VersionGuid="<DatatypeMember<OffsetDataMap HighestAssignedLocalId="<PayloadTokens LStackBitsize=" Info_ClassicPartSize=" Info_VolatilePartSize=" Info_RetainPartSize="<MemberLayout Accessibility=" Info_Array_PaddedSubtypeSize=" Info_Array_SubtypeSize=" ClassicBitoffset=" NenaBitoffset=" OffsetSpecification="<SizeInfo<ParameterPassing<LibraryInfo> TotalMemberCount=" MFlags=" RelativeBitoffset=" Remanence="<Part<Payload><PayloadToken PenaltyBytesInBits=" HighestAssignedInternalId=" SubPartIndex=" Info_WidestMember=" PaddedBitsize="<Value Kind=" Version="2.0" xmlns="<Member ChangeCount=" Value=" RepresentationSize=" PassedAs=" RID=" LID=" Type="Section"B#16#W#16#Ret_ValFunctionCLASSIC_PLEASENENA_PLEASEVoidTruetrueFalsefalseS7_Visible3251:52:53:54:55:58DTLUSIntundefUndefRealHMI_Visible0x02000001WordRetain0x0000FFFFMandatory<Data ID=" Name=" Base=" Relative=" Size=" PaddedSize=" Path="=">
1 change: 1 addition & 0 deletions s7commplus/zlib_dicts/79b2bda3_LineComm_90000001.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<CommentDictionary><InterfaceLineComments><Comment UId="1" LineId="3"><DictEntry Language="en-US">this is a the in to an can be for are network and</DictEntry> </Comment><Comment UId="0" LineId="2"><DictEntry Language="en-US">dies ist ein der die das im nach einen kann sein für sind Netzwerk und</DictEntry> </Comment></InterfaceLineComments><BodyLineComments /> </CommentDictionary><CommentDictionary><InterfaceLineComments /><BodyLineComments /></CommentDictionary><CommentDictionary><InterfaceLineComments /> </CommentDictionary>
1 change: 1 addition & 0 deletions s7commplus/zlib_dicts/81d8db20_IdentES_90000002.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<IdentES version="1.0"> <CoreSubtype>FC</CoreSubtype> <ObjectTypeInfo>CodeBlockData</ObjectTypeInfo> <CompileTime>633706353564000671</CompileTime> <OnlySymbolicAccess>False</OnlySymbolicAccess> <IecCheck>False</IecCheck> <HeaderData Version="0.1" /> <IsSystem>true</IsSystem></IdentES><IdentES version="1.0"> <CoreSubtype>DB</CoreSubtype> <ObjectTypeInfo>DataBlockData</ObjectTypeInfo> <CompileTime>633706353581345975</CompileTime> <DatablockType type="SharedDB" oftype="Undef" ofnumber="0" /> <OnlySymbolicAccess>False</OnlySymbolicAccess> <IecCheck>False</IecCheck> <HeaderData Version="0.1" /> <IsSystem>true</IsSystem></IdentES><IdentES version="1.0"> <CoreSubtype>DB</CoreSubtype> <ObjectTypeInfo>DataBlockData</ObjectTypeInfo> <CompileTime>633706353587127743</CompileTime> <DatablockType type="IDBofSDT" oftype="SDT" ofnumber="0" ofname="IEC_COUNTER" ofTypeTypeGuid="7e93fc34-5398-48b1-a317-38b8cd37b8e8" ofTypeVersionGuid="007ed7c6-51bd-405c-b7e5-e1fd3f25a6c0" /> <OnlySymbolicAccess>True</OnlySymbolicAccess> <IecCheck>True</IecCheck> <HeaderData Version="0.1" /> <IsSystem>false</IsSystem></IdentES><IdentES version="1.0"> <CoreSubtype>OB.ProgramCycle</CoreSubtype> <ObjectTypeInfo>CodeBlockData</ObjectTypeInfo> <CompileTime>633706354424546519</CompileTime> <OnlySymbolicAccess>False</OnlySymbolicAccess> <IecCheck>False</IecCheck> <HeaderData Version="0.1" /> <IsSystem>true</IsSystem></IdentES>
1 change: 1 addition & 0 deletions s7commplus/zlib_dicts/845fc605_NWT_98000001.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<CommentDictionary><NetworkTitles><Comment<DictEntry RefID=" Language="="><CommentDictionary><NetworkTitles></Comment><Comment RefID="0"><DictEntry Language="de-DE"> "Main Program Sweep (Cycle)"</DictEntry></Comment><Comment RefID="2"><DictEntry Language="fr-FR"></DictEntry></Comment><Comment RefID="16"><DictEntry Language="it-IT">this is a the in to an can be for are network and</DictEntry></Comment><Comment RefID="26"><DictEntry Language="en-US">dies ist ein der die das im nach einen kann sein für sind Netzwerk und</DictEntry></NetworkTitles></CommentDictionary>
Loading
Loading