From f696a2e8f62022a8e899e6be63fe6c3744d9bc31 Mon Sep 17 00:00:00 2001 From: SDD Date: Sun, 18 Oct 2026 16:11:24 +0300 Subject: [PATCH] dwt/screenshot: --stretch for the real unit's 1024->1280 display; record geometry and the background stipple --- dwt/evidence/real-unit-1280-white.png | Bin 0 -> 9317 bytes dwt/evidence/real-unit-1280.png | Bin 0 -> 8950 bytes dwt/notes/REAL_UNIT.md | 17 ++++++++++++++++ dwt/screenshot.py | 28 +++++++++++++++++++++++++- 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 dwt/evidence/real-unit-1280-white.png create mode 100644 dwt/evidence/real-unit-1280.png diff --git a/dwt/evidence/real-unit-1280-white.png b/dwt/evidence/real-unit-1280-white.png new file mode 100644 index 0000000000000000000000000000000000000000..697fac1644c30817d000f5b3c5a8529ccb4fc528 GIT binary patch literal 9317 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 tuple[int, int]: + w, h = text.lower().split("x", 1) + return int(w, 0), int(h, 0) + + def parse_rows(text: str) -> tuple[int, int]: if ":" in text: a, b = text.split(":", 1) @@ -82,6 +87,19 @@ def decode(data: bytes, width: int = WIDTH) -> bytearray: return out +def scale_nearest(gray: bytearray, sw: int, sh: int, + dw: int, dh: int) -> bytearray: + """Nearest-neighbour scale; used for the real unit's 1024->1280 display.""" + out = bytearray(dw * dh) + for y in range(dh): + sy = y * sh // dh + src = sy * sw + dst = y * dw + for x in range(dw): + out[dst + x] = gray[src + x * sw // dw] + return out + + def compose_screen(gray: bytearray, rows: int, sw: int, sh: int, ox: int, oy: int) -> bytearray: canvas = bytearray(sw * sh) @@ -147,7 +165,12 @@ def one(rd: exploit.RDebug, a: argparse.Namespace, tag: str) -> None: if a.repeat > 1: root, dot, ext = out.rpartition(".") out = f"{root}-{tag}.{ext}" if dot else f"{out}-{tag}" - if a.screen: + if a.stretch: + dw, dh = a.stretch + scaled = scale_nearest(gray, WIDTH, rows, dw, dh) + write_png(out, dw, dh, scaled) + print(f"[+] {out}: {dw}x{dh} (nearest-neighbour from {WIDTH}x{rows})") + elif a.screen: canvas = compose_screen(gray, rows, a.screen_width, a.screen_height, a.offset_x, a.offset_y) write_png(out, a.screen_width, a.screen_height, canvas) @@ -179,6 +202,9 @@ def main(argv: list[str]) -> int: metavar="A:B", help=f"row range to read (default 0:{HEIGHT})") ap.add_argument("--screen", action="store_true", help="compose the visible screen instead of the raw canvas") + ap.add_argument("--stretch", type=parse_size, metavar="WxH", default=None, + help="nearest-neighbour scale the raw canvas to WxH " + "(the real unit displays 1024x1024 as 1280x1024)") ap.add_argument("--screen-width", type=int, default=SCREEN_W) ap.add_argument("--screen-height", type=int, default=SCREEN_H) ap.add_argument("--offset-x", type=int, default=OFFSET_X)