Skip to content

Commit

Permalink
(#109) parser / generator for MRVN-Radiant compiler signature
Browse files Browse the repository at this point in the history
  • Loading branch information
snake-biscuits committed Jun 14, 2023
1 parent 98726d9 commit 5d92a20
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
60 changes: 60 additions & 0 deletions bsp_tool/extensions/compiler_signature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from __future__ import annotations
from datetime import datetime
import re
from typing import Union


class MRVNRadiant:
motd: str = "Built with love using MRVN-Radiant :)" # remap.h REMAP_MOTD
version: str
time: datetime
_time_format: str = "%a %b %d %H:%M:%S %Y"

def __init__(self, version: str, time: datetime = None, motd=motd):
self.motd = motd
self.version = version
self.time = datetime.now() if time is None else time

def __repr__(self) -> str:
time = self.time.strftime(self._time_format)
return f"<MRVN-Radiant compiler signature (version: {self.version}, time: {time})>"

def __str__(self) -> str:
lines = [self.motd,
f"Version: {self.version}",
f"Time: {self.time.strftime(self._time_format)}"]
return "\n".join(lines)

@classmethod
def from_bytes(cls, raw_signature) -> MRVNRadiant:
motd, version, time = (raw_signature[i * 64:(i + 1) * 64].rstrip(b"\0").decode() for i in range(3))
assert re.match(r"Built with love using MRVN-[rR]adiant :\)", motd)
assert version.startswith("Version:")
version = re.match(r"Version:\s+(.*)", version).groups()[0]
# TODO: if version != "Dev build": parse SemVer & git hash
assert time.startswith("Time:")
time = datetime.strptime(re.match(r"Time:\s+(.*)\n", time).groups()[0], cls._time_format)
# ^ "Wed Jun 07 19:37:23 2023" -> datetime.datetime
return cls(version, time, motd)

def as_bytes(self) -> bytes:
lines = str(self).split("\n")
lines[-1] += "\n"
return b"".join([L.encode() + b"\0" * (64 - len(L)) for L in lines])


AnySignatureClass = Union[MRVNRadiant]


def identify(raw_signature: bytes) -> Union[AnySignatureClass, bytes]:
"""relying on .from_bytes() checks to determine SignatureClass"""
for SignatureClass in (MRVNRadiant,):
try:
signature = SignatureClass.from_bytes(raw_signature)
except Exception:
# AssertionError: some check fails
# AttributeError: re.match returns None
pass
else:
return signature
return raw_signature
26 changes: 26 additions & 0 deletions tests/extensions/test_compiler_signature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""tests for bsp_tool/signature.py"""
from .. import utils
from bsp_tool import RespawnBsp
from bsp_tool.branches.respawn import titanfall2
from bsp_tool.extensions import compiler_signature

import pytest


bsps = utils.get_test_maps(RespawnBsp, {titanfall2: ["Titanfall 2"]})
bsps = [b for b in bsps if b.signature != b""]


@pytest.mark.parametrize("bsp", bsps, ids=[f"Titanfall 2/{b.filename}" for b in bsps])
def test_MRVNRadiant(bsp):
"""parse & replicate"""
signature = compiler_signature.MRVNRadiant.from_bytes(bsp.signature)
assert signature.as_bytes() == bsp.signature


bsp_signature_classes = [(bsp, compiler_signature.MRVNRadiant) for bsp in bsps]


@pytest.mark.parametrize("bsp,SignatureClass", bsp_signature_classes, ids=[f"Titanfall 2/{b.filename}" for b in bsps])
def test_identify(bsp, SignatureClass):
assert isinstance(compiler_signature.identify(bsp.signature), SignatureClass)

0 comments on commit 5d92a20

Please sign in to comment.