feat(rawcli): command catalogs + hints for identified LF chips
catalog_for() returned None for LF, so after identifying a T5577/EM4100 the completer and input_hint had nothing — no command suggestions, no hex/binary opcode hints. Now LF chips get catalogs like the HF ones: - T5577 catalog: READ(block) / WRITE(block, data) / WAKE(password) / DETECT(), with the downlink opcode exposed via build() for hex+binary completion, block-role hints (0=config, 7=password, 1-6=data), and execution via a new run() path (LF isn't a raw-byte exchange — it drives lf.t55 / the demod). READ(0)/DETECT use the reliable rotation-fixed config read; data-block reads are best-effort and labelled as such. capture.read_t55xx_block added. - LF read-only credentials (native EM4100/HID/AWID/FDX-B) get a minimal catalog (INFO -> re-read). - catalog_for dispatches protocol "lf": "T5577" in the label -> T5577, else read-only. - TagCommand gains an optional run(device, *args); completer/_opcode tolerate build=None. Hardware-verified: identify -> catalog T5577, help lists the commands, DETECT()/READ(0) return config 00148040 (EM4100). Tests: LF resolver, T5577 opcodes, T5577 block-role hints. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ from pm3py.cli.rawcli.trace_view import render_exchange
|
||||
from pm3py.cli.rawcli.parser import parse_token, parse_bytes, parse_line
|
||||
from pm3py.cli.rawcli.entry import byte_space
|
||||
from pm3py.cli.rawcli.identify import identify, clear, name_14a, format_summary
|
||||
from pm3py.cli.rawcli.catalog import TYPE2, ISO15, catalog_for, catalog_for as _cf
|
||||
from pm3py.cli.rawcli.catalog import TYPE2, ISO15, T5577, LF_READONLY, catalog_for, catalog_for as _cf
|
||||
from pm3py.cli.rawcli.tlv import parse_tlv, format_tlv
|
||||
from pm3py.cli.rawcli.completer import RawCompleter
|
||||
from pm3py.cli.rawcli.app import dispatch
|
||||
@@ -349,6 +349,22 @@ class TestCatalog:
|
||||
s.protocol, s.transponder = "hf15", "ISO15693"
|
||||
assert catalog_for(s) is ISO15
|
||||
|
||||
def test_resolver_lf(self):
|
||||
# identify sets an LF transponder -> the completer/hints get a catalog, not None
|
||||
s = RawSession()
|
||||
s.protocol, s.transponder = "lf", "T5577 (EM4100 20260716FF)"
|
||||
assert catalog_for(s) is T5577 # command-rich emulator
|
||||
s.transponder = "EM4100 20260716FF" # a native read-only credential
|
||||
assert catalog_for(s) is LF_READONLY
|
||||
|
||||
def test_t5577_opcodes(self):
|
||||
# build() exposes the downlink opcode for hex/binary completion (execution is via run)
|
||||
assert T5577.get("READ").build("0")[0] == 0x01
|
||||
assert T5577.get("WRITE").build("0", "0")[0] == 0x02
|
||||
assert T5577.get("WAKE").build("0")[0] == 0x03
|
||||
assert {c.name for c in T5577.commands.values()} == {"READ", "WRITE", "WAKE", "DETECT"}
|
||||
assert T5577.get("READ").run is not None and LF_READONLY.get("INFO").run is not None
|
||||
|
||||
|
||||
class TestFunctionCalls:
|
||||
def _id(self, sak=0x00):
|
||||
@@ -508,6 +524,17 @@ class TestMemoryHint:
|
||||
from pm3py.cli.rawcli.memory import input_hint
|
||||
assert input_hint(RawSession(), "READ(4)") is None
|
||||
|
||||
def test_t5577_block_roles(self):
|
||||
from pm3py.cli.rawcli.memory import page_role, input_hint
|
||||
tp = "T5577 (EM4100 20260716FF)"
|
||||
assert "config" in page_role(tp, 0)
|
||||
assert "password" in page_role(tp, 7)
|
||||
assert "data" in page_role(tp, 3)
|
||||
s = RawSession()
|
||||
s.protocol, s.transponder = "lf", tp
|
||||
assert "config" in input_hint(s, "READ(0") # block-role relevance for LF too
|
||||
assert "password" in input_hint(s, "WRITE(7,")
|
||||
|
||||
def test_layouts_match_models(self):
|
||||
from pm3py.cli.rawcli.memory import _LAYOUTS
|
||||
from pm3py.sim import NTAG210, NTAG212, NTAG213, NTAG215, NTAG216
|
||||
|
||||
Reference in New Issue
Block a user