destogod/re/chk16.py
type-two 07977624ae DestoGod: replacement for the TP5 bus destination sign software
TP5 is a 2012 Windows application supplied with the Guangzhou-Tongda LED
destination signs fitted to Yutong buses. It has no preview, so building a
destination list is guess-and-check, and it only runs on Windows.

The bus never talks to TP5 — the sign controller reads a .td5 file off an SD
card, and that is the whole interface. So this replaces the software without
touching any hardware or protocol: it just has to write byte-correct .td5.

Formats reverse-engineered from the sample files and TP5(En).exe, then verified
byte-for-byte:

  .td5   the file the bus reads. Fixed-layout binary; each destination block
         carries a CRC-16/ARC over block[3..len] and a rand() block id, which
         together looked like one 4-byte field because RAND_MAX is 0x7fff.
  .tp5   the editable project. Line-based text, UTF-16BE hex strings.
  .font  the sign's own bitmap fonts, each glyph row XORed with its char code.

The app is one self-contained HTML file: live LED preview at the real sign size
with real scrolling, spreadsheet/CSV import, multi-page destinations, undoable
delete, and export to both .td5 and .tp5.

Verified:
  - rebuilds a real 46,080-byte TP5 export byte-for-byte with a recomputed CRC
  - all 36 stored CRCs verify against the implementation
  - driven through its own UI, re-exporting the real file differs in 7 bytes,
    all of them the export timestamp
  - running on a real bus: signs and driver's controller both correct

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:06:25 +10:00

29 lines
798 B
Python

import struct
from parse import records
rs = records()
for r in rs:
b = r['blk']
r['chk'] = struct.unpack_from('<H', b, 1)[0]
r['chkbe'] = struct.unpack_from('>H', b, 1)[0]
r['rnd'] = struct.unpack_from('<I', b, 3)[0]
print("%-18s %-6s %-6s %-6s %6s" % ('name','chkLE','chkBE','rand','blen'))
for r in rs:
print("%-18s %04x %04x %04x %6d" % (r['name'], r['chk'], r['chkbe'], r['rnd'], r['blklen']))
print()
# simple sum tests
def sums(b):
return dict(
sum_all=sum(b)&0xffff,
sum_3=sum(b[3:])&0xffff,
sum_7=sum(b[7:])&0xffff,
sum_116=sum(b[0x116:])&0xffff,
sum_z=(sum(b)-b[1]-b[2])&0xffff,
xor_all=0,
)
for r in rs[:6]:
b=r['blk']
print(r['name'], '%04x'%r['chk'], {k:'%04x'%v for k,v in sums(b).items()})