49 lines
1.7 KiB
Python
49 lines
1.7 KiB
Python
|
"""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
|