|
|
|
@ -14,7 +14,7 @@ except ModuleNotFoundError:
|
|
|
|
|
from .packer import Packer |
|
|
|
|
from .const import ( |
|
|
|
|
NAME_MAX_COMPRESSED, NAME_MAX_DECOMPRESSED, ATTR, PACK_ALPHABET, END_NODE, END_DOC, ARRAY_BIT, |
|
|
|
|
ENCODING, CONTENT, CONTENT_COMP, CONTENT_FULL, XML_ENCODING, Type |
|
|
|
|
ENCODING, CONTENT, CONTENT_COMP, CONTENT_FULL, XML_ENCODING, DEFAULT_ENCODING, Type |
|
|
|
|
) |
|
|
|
|
from .misc import unpack, py_encoding, assert_true |
|
|
|
|
from .node import XMLNode |
|
|
|
@ -45,7 +45,7 @@ class Decoder:
|
|
|
|
|
if self.packer: |
|
|
|
|
self.packer.notify_skipped(length) |
|
|
|
|
raw = self.stream.read(length) |
|
|
|
|
return raw.decode(py_encoding(self.encoding)).rstrip("\0") |
|
|
|
|
return raw.decode(py_encoding(self.encoding or DEFAULT_ENCODING)).rstrip("\0") |
|
|
|
|
|
|
|
|
|
length = struct.calcsize("=" + s_format) |
|
|
|
|
if self.packer and align: |
|
|
|
@ -55,22 +55,27 @@ class Decoder:
|
|
|
|
|
value = struct.unpack(">" + s_format, data) |
|
|
|
|
return value[0] if single else value |
|
|
|
|
|
|
|
|
|
def _read_node_value(self, node): |
|
|
|
|
def _read_node_value(self, node: XMLNode) -> None: |
|
|
|
|
fmt = node.type.value.fmt |
|
|
|
|
count = 1 |
|
|
|
|
if node.is_array: |
|
|
|
|
length = struct.calcsize("=" + fmt) |
|
|
|
|
count = self.read("I") // length |
|
|
|
|
nbytes = self.read("I") |
|
|
|
|
assert isinstance(nbytes, int) |
|
|
|
|
count = nbytes // length |
|
|
|
|
values = [] |
|
|
|
|
for _ in range(count): |
|
|
|
|
values.append(self.read(fmt, single=len(fmt) == 1, align=False)) |
|
|
|
|
self.packer.notify_skipped(count * length) |
|
|
|
|
return values |
|
|
|
|
|
|
|
|
|
node.value = self.read(fmt, single=len(fmt) == 1) |
|
|
|
|
assert self.packer is not None |
|
|
|
|
self.packer.notify_skipped(count * length) |
|
|
|
|
node.value = values |
|
|
|
|
else: |
|
|
|
|
node.value = self.read(fmt, single=len(fmt) == 1) |
|
|
|
|
|
|
|
|
|
def _read_metadata_name(self): |
|
|
|
|
def _read_metadata_name(self) -> str: |
|
|
|
|
length = self.read("B") |
|
|
|
|
assert isinstance(length, int) |
|
|
|
|
|
|
|
|
|
if not self.compressed: |
|
|
|
|
if length < 0x80: |
|
|
|
@ -78,14 +83,16 @@ class Decoder:
|
|
|
|
|
# i.e. length = (length & ~0x40) + 1 |
|
|
|
|
length -= 0x3f |
|
|
|
|
else: |
|
|
|
|
length = (length << 8) | self.read("B") |
|
|
|
|
extra = self.read("B") |
|
|
|
|
assert isinstance(extra, int) |
|
|
|
|
length = (length << 8) | extra |
|
|
|
|
# i.e. length = (length & ~0x8000) + 0x41 |
|
|
|
|
length -= 0x7fbf |
|
|
|
|
assert_true(length <= NAME_MAX_DECOMPRESSED, "Name length too long", DecodeError) |
|
|
|
|
|
|
|
|
|
name = self.stream.read(length) |
|
|
|
|
assert_true(len(name) == length, "Not enough bytes to read name", DecodeError) |
|
|
|
|
return name.decode(self.encoding) |
|
|
|
|
return name.decode(self.encoding or "") |
|
|
|
|
|
|
|
|
|
out = "" |
|
|
|
|
if length == 0: |
|
|
|
@ -99,7 +106,7 @@ class Decoder:
|
|
|
|
|
|
|
|
|
|
def _read_metadata(self, type_): |
|
|
|
|
name = self._read_metadata_name() |
|
|
|
|
node = XMLNode(name, type_, None, encoding=self.encoding) |
|
|
|
|
node = XMLNode(name, type_, None, encoding=self.encoding or DEFAULT_ENCODING) |
|
|
|
|
|
|
|
|
|
while (child := self.read("B")) != END_NODE: |
|
|
|
|
if child == ATTR: |
|
|
|
@ -109,8 +116,8 @@ class Decoder:
|
|
|
|
|
node.children.append(attr) |
|
|
|
|
else: |
|
|
|
|
node.children.append(self._read_metadata(child)) |
|
|
|
|
is_array = not not (type_ & ARRAY_BIT) |
|
|
|
|
if is_array: |
|
|
|
|
|
|
|
|
|
if type_ & ARRAY_BIT: |
|
|
|
|
node.value = [] |
|
|
|
|
return node |
|
|
|
|
|
|
|
|
@ -140,6 +147,8 @@ class Decoder:
|
|
|
|
|
|
|
|
|
|
def _read_xml_string(self): |
|
|
|
|
assert_true(etree is not None, "lxml missing", DecodeError) |
|
|
|
|
assert etree is not None |
|
|
|
|
|
|
|
|
|
parser = etree.XMLParser(remove_comments=True) |
|
|
|
|
tree = etree.XML(self.stream.read(), parser) |
|
|
|
|
self.encoding = XML_ENCODING[tree.getroottree().docinfo.encoding.upper()] |
|
|
|
@ -164,7 +173,11 @@ class Decoder:
|
|
|
|
|
d_type = type_.value |
|
|
|
|
|
|
|
|
|
if d_type.size == 1 and not is_array: |
|
|
|
|
value = d_type._parse(node.text or "") |
|
|
|
|
try: |
|
|
|
|
value = d_type._parse(node.text or "") |
|
|
|
|
except ValueError: |
|
|
|
|
print(f"Failed to parse {node.tag} ({d_type.names[0]}): {repr(node.text)}") |
|
|
|
|
raise |
|
|
|
|
else: |
|
|
|
|
data = node.text.split(" ") |
|
|
|
|
|
|
|
|
@ -174,7 +187,7 @@ class Decoder:
|
|
|
|
|
if not is_array: |
|
|
|
|
value = value[0] |
|
|
|
|
|
|
|
|
|
xml_node = XMLNode(node.tag, type_, value, encoding=self.encoding) |
|
|
|
|
xml_node = XMLNode(node.tag, type_, value, encoding=self.encoding or DEFAULT_ENCODING) |
|
|
|
|
for i in node.getchildren(): |
|
|
|
|
xml_node.children.append(walk(i)) |
|
|
|
|
|
|
|
|
@ -198,21 +211,27 @@ class Decoder:
|
|
|
|
|
self._read_magic() |
|
|
|
|
|
|
|
|
|
header_len = self.read("I") |
|
|
|
|
assert isinstance(header_len, int) |
|
|
|
|
start = self.stream.tell() |
|
|
|
|
schema = self._read_metadata(self.read("B")) |
|
|
|
|
assert_true(self.read("B") == END_DOC, "Unterminated schema", DecodeError) |
|
|
|
|
padding = header_len - (self.stream.tell() - start) |
|
|
|
|
assert_true(padding >= 0, "Invalid schema definition", DecodeError) |
|
|
|
|
assert_true(all(i == 0 for i in self.stream.read(padding)), "Invalid schema padding", DecodeError) |
|
|
|
|
assert_true( |
|
|
|
|
all(i == 0 for i in self.stream.read(padding)), "Invalid schema padding", DecodeError |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
body_len = self.read("I") |
|
|
|
|
assert isinstance(body_len, int) |
|
|
|
|
start = self.stream.tell() |
|
|
|
|
self.packer = Packer(start) |
|
|
|
|
data = self._read_databody(schema) |
|
|
|
|
self.stream.seek(self.packer.request_allocation(0)) |
|
|
|
|
padding = body_len - (self.stream.tell() - start) |
|
|
|
|
assert_true(padding >= 0, "Data shape not match schema", DecodeError) |
|
|
|
|
assert_true(all(i == 0 for i in self.stream.read(padding)), "Invalid data padding", DecodeError) |
|
|
|
|
assert_true( |
|
|
|
|
all(i == 0 for i in self.stream.read(padding)), "Invalid data padding", DecodeError |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
assert_true(self.stream.read(1) == b"", "Trailing data unconsumed", DecodeError) |
|
|
|
|
|
|
|
|
|