"""Tools for parsing a ROE file.""" from os import SEEK_CUR, SEEK_SET from os.path import basename, splitext from struct import unpack from .exceptions import InvalidROEFileError from .utils import nibble_flip class Roe: """The DNA of an electronic fish with an embedded icon.""" def __init__(self, name: str = "", author: str | None = None) -> None: self.name: str = name self.author: str | None = author self.is_mutant: bool = False self.unknowns: dict[str, bytes] = {} @classmethod def from_file(cls, filename: str) -> "Roe": """Factory for importing roe from a .ROE file.""" # Unlike .FSH files, the name comes from the filename itself. Since # DOS was limited to the 8.3 naming convention, a fish's name has a # forced limit of only eight characters. name = splitext(basename(filename))[0][0:8] roe = cls(name) with open(filename, "rb") as rfile: # First byte of file determines mutant status. Non-zero = mutant. # Second byte is padding. roe.is_mutant = unpack("?x", rfile.read(2))[0] # Next two bytes are unknown. roe.unknowns["00000002"] = unpack("2s", rfile.read(2))[0] # Next sixteen bytes is a nibble-flipped encoding of the original # author with a zero null termination byte. author = "" flipped_author = unpack("16s", rfile.read(16))[0] for letter in flipped_author: code = nibble_flip(letter) if code == 0: break author += chr(code) roe.author = author.upper() return roe