Skip to content

skip_section truncates unrecognized sections at the first '#' in their body (silent data loss / misparse) #5

Description

@ctoth

skip_section retains an unrecognized section only up to the first # that appears anywhere in its body:

https://github.com/ctoth/area_reader/blob/3297b88/area_reader/__init__.py#L339-L341

def skip_section(self, section_name):
    logger.debug("Skipping section %s", section_name)
    self.skipped_sections.append((section_name, self.read_until('#')))

read_until('#') matches a # in ordinary section content (help text, social messages, mobprog code, OLC extension sections), not just the next section header. After the truncated skip, load_sections resumes parsing mid-section, treating whatever follows the embedded # as a section name. README's guarantee — "unrecognized source sections are retained as native sections" — does not hold for any skipped section whose body contains #.

Failure modes (both reproduced)

1. Embedded #$ silently terminates the whole load — data loss with no error.

area_a = ("#AREA\nfile.are~\nName~\nmeta~\n0 0\n\n"
          "#SOCIALS\nsmile leans back and thinks of #$ money.\n\n"
          "#HELPS\n5 greeting~\nhello~\n0 $~\n\n#$\n")
f = RomAreaFile(path_to(area_a)); f.load_sections()

Output:

(a) helps loaded: 0 (expected 1; 0 means silent data loss)
(a) skipped: [('socials', '\nsmile leans back and thinks of ')]

The #HELPS section after the skipped #SOCIALS is never loaded, the social text is truncated, and no exception is raised.

2. An embedded token matching a known section name is parsed as real data.

area_b = ("#AREA\nfile.are~\nName~\nmeta~\n0 0\n\n"
          "#NOTES\nsee the #HELPS section for 42 more~\ninfo~\n0 $~\n\n"
          "#$\n")

Output:

(b) ParseError: ...\b.are line 8 col 15 in section helps: Error reading section 'helps'

The #HELPS inside the skipped #NOTES body is treated as a live HELPS section and parsing fails (or, with luckier content, loads garbage helps silently).

A related case that mostly survives by accident: ROM 2.4b6 #MOBPROGS sections contain #vnum record markers, so they get split into a chain of phantom skipped sections whose concatenation happens to reproduce the bytes — until the program text contains $ or a known section word after a #.

Suggested direction

Skipping needs to be section-aware rather than a raw scan to the next #: only treat # as a section boundary when it starts a line (matching how the engines' loaders read section headers), and/or track tilde-string state while scanning. The existing writer-side round-trip tests (test_rom_writer.py etc.) only cover skipped bodies without embedded #, e.g. ("socials", "\nwave wildly~\n"), so a regression test with # inside the body would lock in the fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions