Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type annotate the ndspy.bmg module #10

Merged
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ This section will try to answer some questions you may have.
Installation
------------

ndspy requires Python 3.6 or newer to run. CPython (the reference
ndspy requires Python 3.7 or newer to run. CPython (the reference
implementation of Python) and PyPy are both supported. Python 2, though, is not
supported at all.

Expand Down
84 changes: 57 additions & 27 deletions ndspy/bmg.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@
"""
Support for BMG files.
"""
from __future__ import annotations


import os
import struct
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Literal

from . import _common

Expand All @@ -35,8 +40,19 @@ class BMG:
"""
A class representing a BMG file.
"""
messages: list[Message]
instructions: list[bytes]
labels: list[tuple[int, int]]
scripts: list[tuple[int, int]]

def __init__(self, data=None, *, id=0):
id: int
encoding: Literal['cp1252', 'utf-16', 'shift-jis', 'utf-8']
endianness: Literal['<', '>']
unk14: int
unk18: int
unk1C: int

def __init__(self, data: bytes | None = None, *, id: int = 0):

self.messages = []
self.instructions = []
Expand All @@ -56,13 +72,13 @@ def __init__(self, data=None, *, id=0):


@property
def fullEncoding(self):
def fullEncoding(self) -> str:
if self.encoding.lower() == 'utf-16':
return 'utf-16' + ('le' if self.endianness == '<' else 'be')
return self.encoding


def _initFromData(self, data):
def _initFromData(self, data: bytes) -> None:
if data[:8] != b'MESGbmg1':
raise ValueError('Not a BMG file.')

Expand All @@ -84,7 +100,7 @@ def _initFromData(self, data):
raise ValueError(f'Unknown encoding value: {enc}')

INF1 = []
def parseINF1(offset, length):
def parseINF1(offset: int, length: int) -> None:
count, entryLength, self.id = struct.unpack_from(se + 'HHI', data, offset + 8)

for i in range(count):
Expand All @@ -94,13 +110,13 @@ def parseINF1(offset, length):
INF1.append((entryOff, entryAttribs))

DAT1 = b''
def parseDAT1(offset, length):
def parseDAT1(offset: int, length: int) -> None:
nonlocal DAT1
DAT1 = data[offset + 8 : offset + length]

self.instructions = []
self.labels = []
def parseFLW1(offset, length):
def parseFLW1(offset: int, length: int) -> None:
instructionsCount, labelsCount, unk0C = \
struct.unpack_from(se + 'HHI', data, offset + 8)
# unk0C is always 0, as far as I can tell
Expand All @@ -121,7 +137,7 @@ def parseFLW1(offset, length):
self.labels.append((bmgID, index))

self.scripts = []
def parseFLI1(offset, length):
def parseFLI1(offset: int, length: int) -> None:
count, entryLength, unk0C = struct.unpack_from(se + 'HHI', data, offset + 8)
assert entryLength == 8, f'Unexpected FLI1 entry length ({entryLength})'
# unk0C is always 0, as far as I can tell
Expand Down Expand Up @@ -183,9 +199,15 @@ def parseFLI1(offset, length):


@classmethod
def fromMessages(cls, messages,
instructions=None, labels=None, scripts=None,
*, id=0):
def fromMessages(
cls,
messages: list[Message],
instructions: list[bytes] | None = None,
labels: list[tuple[int, int]] | None = None,
scripts: list[tuple[int, int]] | None = None,
*,
id: int = 0,
):
"""
Create a BMG from a list of messages.
"""
Expand All @@ -203,15 +225,15 @@ def fromMessages(cls, messages,


@classmethod
def fromFile(cls, filePath, *args, **kwargs):
def fromFile(cls, filePath: str | os.PathLike, *args, **kwargs) -> BMG:
"""
Load a BMG from a filesystem file.
"""
with open(filePath, 'rb') as f:
return cls(f.read(), *args, **kwargs)


def save(self):
def save(self) -> bytes:
"""
Generate file data representing this BMG.
"""
Expand Down Expand Up @@ -315,7 +337,7 @@ def save(self):
return bytes(data)


def saveToFile(self, filePath):
def saveToFile(self, filePath: str | os.PathLike) -> None:
"""
Generate file data representing this BMG, and save it to a
filesystem file.
Expand All @@ -325,13 +347,13 @@ def saveToFile(self, filePath):
f.write(d)


def __str__(self):
def __str__(self) -> str:
return (f'<bmg id={self.id} '
f'({len(self.messages)} messages, '
f'{len(self.scripts)} scripts)>')


def __repr__(self):
def __repr__(self) -> str:
args = [repr(self.messages)]

if self.instructions:
Expand Down Expand Up @@ -366,15 +388,18 @@ class Escape:
type and optional parameter data.
"""

def __init__(self, type=0, data=b''):
type: int
data: bytes

def __init__(self, type: int = 0, data: bytes = b''):
self.type = type
self.data = data

# Type = 4 is used for pluralization in Spirit Tracks
# (and there are a couple parameters -- need to look into
# that)

def save(self, encoding):
def save(self, encoding: str) -> bytes:
"""
Generate binary data representing this escape sequence.
"""
Expand All @@ -385,17 +410,22 @@ def save(self, encoding):
data.extend(self.data)
return data

def __repr__(self):
def __repr__(self) -> str:
return f'{type(self).__name__}({self.type!r}, {self.data!r})'

def __str__(self):
def __str__(self) -> str:
return f'[{self.type}:{self.data.hex()}]'

info = 0
stringParts = None
isNull = False
info: bytes
stringParts: list[str | Escape]
isNull: bool

def __init__(self, info=b'', stringParts=None, isNull=False):
def __init__(
self,
info: bytes = b"",
stringParts: str | list[str] | None = None,
isNull: bool = False,
):
# If a single string is passed in, put it in a list for convenience
if isinstance(stringParts, str):
stringParts = [stringParts]
Expand All @@ -404,7 +434,7 @@ def __init__(self, info=b'', stringParts=None, isNull=False):
self.stringParts = [] if stringParts is None else stringParts
self.isNull = isNull

def save(self, encoding):
def save(self, encoding: str) -> bytes:
"""
Generate binary data representing this message.
"""
Expand All @@ -423,8 +453,8 @@ def save(self, encoding):
data.extend('\0'.encode(encoding))
return data

def __repr__(self):
def __repr__(self) -> str:
return f'{type(self).__name__}({self.info!r}, {self.stringParts!r})'

def __str__(self):
def __str__(self) -> str:
return ''.join(str(s) for s in self.stringParts)
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
long_description_content_type='text/markdown',
url='https://github.com/RoadrunnerWMC/ndspy',
packages=setuptools.find_packages(),
python_requires='>=3.6',
python_requires='>=3.7',
entry_points={
'console_scripts': [
'ndspy_codeCompression = ndspy.codeCompression:main',
'ndspy_lz10 = ndspy.lz10:main',
],
},
classifiers=[
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
Expand Down