Skip to content

Commit

Permalink
Reparse file in case of cross-reference
Browse files Browse the repository at this point in the history
  • Loading branch information
Dvd848 committed Oct 7, 2021
1 parent 69abb49 commit 3349aef
Show file tree
Hide file tree
Showing 3 changed files with 1,452 additions and 1,428 deletions.
15 changes: 15 additions & 0 deletions pytai/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@
from .common import *
from . import utils

class PytaiUnparsedAccessException(Exception):
"""The parser tried to access an element that hasn't been parsed yet.
This can happen when the file format defines cross-references between
structures, i.e. one structure contains an element that isn't relative to
itself, but to another structure.
Upon catching such an exception, the caller can choose to raise a flag
and continue parsing the file. After proceeding with the parsing until the end,
the caller can restart the parsing (if the flag is raised) in hope that that
the cross reference has been resolved (i.e. the referenced element has been parsed).
The caller should set a limit to the amount of reparse attempts.
"""

class Parser(object):
ChildAttr = namedtuple("ParserChildAttr", "name value start_offset end_offset is_metavar is_array")
Expand Down Expand Up @@ -226,6 +239,8 @@ def _get_details(self, debug_dict: Dict[str, Dict[str, int]], parent: "KaitaiStr
is_array = True
except KeyError:
pass
except AttributeError:
raise PytaiUnparsedAccessException(f"Unparsed access during parsing of '{child_name}' ")

return start_offset, end_offset, is_array, value

Expand Down
51 changes: 30 additions & 21 deletions pytai/tests/kaitai_to_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ def kaitai_to_xml(parser: "KaitaiParser", path: str) -> ET.ElementTree:
XML representation of the file structure.
"""

root = ET.Element("root")
reparse_needed = False

def recurse(parent_object: Union[List["KaitaiStruct"], "KaitaiStruct"], parent_node: ET.Element, is_array: bool) -> None:
"""Recursive function to built the XML tree.
Expand All @@ -70,26 +69,36 @@ def recurse(parent_object: Union[List["KaitaiStruct"], "KaitaiStruct"], parent_n
True if parent_object is a list, false otherwise.
"""
if is_array:
values = parent_object
else:
values = parser.get_children(parent_object)

for child_attr in values:


current_node = ET.SubElement(parent_node, "node", name = child_attr.name,
extra_info = parser.get_item_description(child_attr.value),
start_offset = str(child_attr.start_offset),
end_offset = str(child_attr.end_offset),
is_metavar = str(child_attr.is_metavar))
recurse(child_attr.value, current_node, child_attr.is_array)


nonlocal reparse_needed

try:
if is_array:
values = parent_object
else:
values = parser.get_children(parent_object)

for child_attr in values:
current_node = ET.SubElement(parent_node, "node", name = child_attr.name,
extra_info = parser.get_item_description(child_attr.value),
start_offset = str(child_attr.start_offset),
end_offset = str(child_attr.end_offset),
is_metavar = str(child_attr.is_metavar))
recurse(child_attr.value, current_node, child_attr.is_array)
except model.PytaiUnparsedAccessException:
reparse_needed = True

max_retries = 5
with parser.parse(path) as parsed_file:
recurse(parent_object = parsed_file, parent_node = root, is_array = False)

return root
for _ in range(max_retries):
root = ET.Element("root")
recurse(parent_object = parsed_file, parent_node = root, is_array = False)
if reparse_needed:
reparse_needed = False
# Try again
else:
return root

raise RuntimeError(f"Could not parse {path}")

def save_kaitai_to_xml(kaitai_format: str, input_path: Union[str, Path], output_path: Union[str, Path]) -> None:
"""Save an XML representation of the given file to the given output path.
Expand Down
Loading

0 comments on commit 3349aef

Please sign in to comment.