- Crash on raw hex: reader raw() returns `data` as a hex STRING; the raw
path fed that to bytes() -> "string argument without an encoding".
_raw_exchange now returns the bytes (`raw` field, or converts `data`);
render_exchange accepts bytes or a hex string.
- Completion for numeric entry, transponder-dependent: typing hex ("60")
or binary ("01100000") now matches the identified tag's command opcodes
and surfaces the named command (GET_VERSION) with its help. Honours a
0x/0b prefix and the current entry mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
1.3 KiB
Python
32 lines
1.3 KiB
Python
"""Render a raw reader↔tag exchange as annotated, Proxmark-style trace lines, reusing the
|
|
existing :class:`pm3py.trace.TraceFormatter` and the per-protocol decoders."""
|
|
from __future__ import annotations
|
|
|
|
from pm3py.trace import TraceFormatter, decode_14443a, decode_15693
|
|
|
|
#: protocol key -> (annotation decoder, CRC byte length)
|
|
_PROTOCOLS = {
|
|
"hf14a": (decode_14443a, 2),
|
|
"hf15": (decode_15693, 2),
|
|
"lf": (None, 0),
|
|
}
|
|
|
|
|
|
def _as_bytes(value) -> bytes:
|
|
"""Accept bytes or a hex string (which is what the reader `raw()` returns as `data`)."""
|
|
if isinstance(value, str):
|
|
return bytes.fromhex(value.replace(" ", ""))
|
|
return bytes(value)
|
|
|
|
|
|
def render_exchange(command, response, *, protocol: str = "hf14a", is_tty: bool | None = None) -> str:
|
|
"""Return the annotated trace for one exchange: a ``Reader → Tag`` line for ``command`` and,
|
|
if present, a ``Tag → Reader`` line for ``response``. Both accept bytes or a hex string.
|
|
``is_tty`` forces/suppresses ANSI."""
|
|
decoder, crc_len = _PROTOCOLS.get(protocol, (None, 0))
|
|
fmt = TraceFormatter(mode="reader", decoder=decoder, crc_len=crc_len, is_tty=is_tty)
|
|
lines = [fmt.format(0, _as_bytes(command)).strip("\n")]
|
|
if response:
|
|
lines.append(fmt.format(1, _as_bytes(response)).strip("\n"))
|
|
return "\n".join(lines)
|