74 lines
1.9 KiB
Python
74 lines
1.9 KiB
Python
import inspect
|
|
import re
|
|
|
|
from typing import Type
|
|
|
|
from .exception import CheckFailed, InvalidModel
|
|
|
|
|
|
def assert_true(check: bool, reason: str, exc: Type[Exception] = CheckFailed):
|
|
if not check:
|
|
line = inspect.stack()[1].code_context
|
|
if line:
|
|
print()
|
|
print("\n".join(line))
|
|
raise exc(reason)
|
|
|
|
|
|
def py_encoding(name: str) -> str:
|
|
if name.startswith("shift-jis"):
|
|
return "shift-jis"
|
|
return name
|
|
|
|
|
|
def parse_model(model: str) -> tuple[str, str, str, str, str]:
|
|
# e.g. KFC:J:A:A:2019020600
|
|
match = re.match(r"^([A-Z0-9]{3}):([A-Z]):([A-Z]):([A-Z])(?::(\d{10}))?$", model)
|
|
if match is None:
|
|
raise InvalidModel
|
|
gamecode, dest, spec, rev, datecode = match.groups()
|
|
return gamecode, dest, spec, rev, datecode
|
|
|
|
|
|
def pack(data, width: int) -> bytes:
|
|
assert_true(1 <= width <= 8, "Invalid pack size")
|
|
assert_true(all(i < (1 << width) for i in data), "Data too large for packing")
|
|
bit_buf = in_buf = 0
|
|
output = bytearray()
|
|
for i in data:
|
|
bit_buf |= i << (8 - width)
|
|
shift = min(8 - in_buf, width)
|
|
bit_buf <<= shift
|
|
in_buf += shift
|
|
if in_buf == 8:
|
|
output.append(bit_buf >> 8)
|
|
in_buf = width - shift
|
|
bit_buf = (bit_buf & 0xff) << in_buf
|
|
|
|
if in_buf:
|
|
output.append(bit_buf >> in_buf)
|
|
|
|
return bytes(output)
|
|
|
|
|
|
def unpack(data, width: int) -> bytes:
|
|
assert_true(1 <= width <= 8, "Invalid pack size")
|
|
bit_buf = in_buf = 0
|
|
output = bytearray()
|
|
for i in data:
|
|
bit_buf |= i
|
|
bit_buf <<= width - in_buf
|
|
in_buf += 8
|
|
while in_buf >= width:
|
|
output.append(bit_buf >> 8)
|
|
in_buf -= width
|
|
bit_buf = (bit_buf & 0xff) << min(width, in_buf)
|
|
|
|
if in_buf:
|
|
output.append(bit_buf >> (8 + in_buf - width))
|
|
|
|
return bytes(output)
|
|
|
|
|
|
__all__ = ("assert_true", "py_encoding", "parse_model", "pack", "unpack")
|