23 KiB
23 KiB
MAG160C Reverse Engineering Progress
2026-07-12
- Started task.
- Read relevant process skills: using-superpowers, brainstorming, planning-with-files, test-driven-development, systematic-debugging, verification-before-completion.
- Confirmed current directory is not a git repository.
- Ran initial file inventory with rg --files; found Windows C# examples, native SDK headers/DLLs, Android demo/AARs, native libraries, and libuvc/libusb source.
- Created persistent planning files.
- Extracted SDK PDF text with Python/pdfplumber.
- Read Windows headers and C# wrappers.
- Read Android demo source and AAR/class signatures using javap.
- Extracted thermallib-release.aar to .reverse/thermallib_aar for analysis.
- Confirmed IR camera VID/PID 0x833C/0x0001 from Hardware Guide.
- Confirmed Android native libcoresdk.so exports MAG_* APIs and uses libusb.
- Attempted to install capstone/pyelftools into .tools/python-revlibs; failed because network/proxy is unavailable. No new reverse-engineering software installed.
- Tried PowerShell Get-PnpDevice for attached hardware; access denied in current environment.
- Tried MinGW objdump on ARM64 libcoresdk.so; unsupported architecture.
- Resumed from planning files and continued Android bytecode inspection with javap.
- Found detailed FTDI/TCM command framing in FTDICommand and confirmed Android TCM bulk communication auto-detects an interface with bulk IN + bulk OUT endpoints.
- Expanded TCM command table for motor, proximity, light, power, audio, schedule task, and firmware update commands.
- Extracted MagDevice JNI method surface and core native data structures from coresdk.jar.
- Inspected Windows ThermalSDK.dll, CameraSDK.dll, and CoreSDKLib.dll exports/imports/strings.
- Confirmed ThermalSDK.dll wraps CoreSDKLib.dll MAG_* APIs, CoreSDKLib.dll imports libusb0 bulk read/write APIs, and Windows TCM logs match the Android TCM command frame model.
- Marked reverse-engineering evidence phases 3 and 4 complete; moved into design approval phase.
- User selected Linux deliverables: C/C++ library plus CLI sample, and Python-callable tooling/bindings.
- User approved implementation approach 1: C++ core library + stable C ABI + CLI + Python ctypes/cffi-style wrapper.
- Wrote design spec at docs/superpowers/specs/2026-07-12-mag160c-linux-sdk-design.md and ran a placeholder scan/self-review.
- Did not commit the design spec because the workspace is not a git repository; progress remains tracked in markdown files.
- User approved the design/spec and requested the implementation plan.
- Wrote implementation plan at docs/superpowers/plans/2026-07-12-mag160c-linux-sdk.md.
- Ran implementation-plan self-review/placeholder scan; no red-flag placeholders were found.
- Implemented Task 1 scaffold: CMake, public C ABI lifecycle, error names, and lifecycle tests.
- Task 1 verification note: cmake is unavailable in PATH, so substituted direct MinGW g++ compile/run verification with MAG160C_STATIC and MAG160C_HAS_LIBUSB=0; test executable passed with exit code 0.
- Implemented Task 2 TCM frame codec with byte-exact encode/decode tests and C ABI utility functions.
- Task 2 verification note: cmake remains unavailable in PATH, so substituted direct MinGW g++ compile/run verification for test_c_api and test_tcm_frame; both passed with exit code 0.
- Implemented Task 3 TCM command builder, dry-run C ABI helpers, and command payload tests.
- Task 3 verification note: cmake remains unavailable in PATH, so substituted direct MinGW g++ compile/run verification for test_c_api, test_tcm_frame, and test_tcm_device; all passed with exit code 0.
- Implemented Task 4 USB device model, endpoint-pair selection, and C ABI device listing/freeing.
- Task 4 verification note: cmake remains unavailable in PATH, so substituted direct MinGW g++ compile/run verification for test_c_api and test_device_model; both passed with exit code 0.
2026-07-14 Handoff
- User asked to stop and then requested a compact context handoff for a new conversation.
- Updated
task_plan.md,findings.md, andprogress.mdwith a short resume-ready summary. - Current implementation state to remember:
- design and plan are already approved
- Tasks 1-4 are implemented and verified in the no-libusb fallback path
- remaining work is the live Linux runtime path, CLI sample, and Python wrapper integration
- Important stop condition:
- do not continue execution unless the user explicitly asks to resume work
2026-07-14 Resume
- User explicitly resumed the task and added a Linux SDK under
IR_Camera_SDK-1.0.1/linux. - Restored context from
task_plan.md,findings.md, andprogress.md. - Attempted planning-with-files session catchup via
.claudeskill path; it failed becauseC:\Users\ZXC\.claude\skills\planning-with-files\scripts\session-catchup.pydoes not exist in this Codex install. - Verified the workspace is still not a git repository, so implementation continues in-place with markdown progress tracking.
- Began Linux SDK inventory. Found versioned Linux packages for 1.0.0, 1.0.1, 1.2.1, and 1.2.2; 1.2.2 is extracted with Qt demo sources, headers, Linux shared libraries, and firmware.
- Read Linux 1.2.2
CHANGELOG,Coding Tips,ThermalSDK.h,CameraSDK.h,Def.h, and Qt project file. - Inspected Linux
libthermalSDK.soandlibmagcore.so.2.1.1ELF symbols/strings. ConfirmedlibmagcoreexportsMAG_*plus bundledlibusb_*; confirmed Linux TCM implementation usesSerialPort/TCMDevice::open(std::string)classes around the same FTDI command concept. - Extracted text from Linux
SDK Guide.pdfandQuick Start.pdfafter two PowerShell quoting failures; root cause was Bash-style heredoc/Python newline quoting in PowerShell, not PDF tooling. - Read Linux Qt demo
basewidget.cpp,testwidget.cpp,expertwidget.cpp, andsmartwidget.cpp; recorded startup/callback/calibration usage in findings. - Disassembled small Linux x86-64 slices of
MAG_LinkCamera,MAG_LinkCameraEx,MAG_StartProcessImage,MAG_TriggerFFC,MAG_EnumCameras,MAG_GetCamInfo,MAG_SetFFCMode,EloMotorDevice::rotate, andTCMVirtualDevice::sendDatato extract interoperability facts only. - New low-level Linux evidence recorded: IR endpoints
0x03OUT,0x81/0x82/0x84IN; raw stream markers0x1bb1b11b/0x1bb1b11c;MAG_LinkCameradefault VID0x833c; Linux rotate clamp[-250,250]while Android evidence had[-128,128]. - Implemented Task 5 IR API skeleton with public
mag160c_ir_info_t,mag160c_ir_open_first,mag160c_ir_close,mag160c_ir_get_info,mag160c_ir_trigger_ffc, andmag160c_ir_read_raw_once. - Task 5 RED verification: direct MinGW compile of
tests/cpp/test_ir_skeleton.cppfailed as expected becausemag160c_ir_*andmag160c_ir_info_twere not declared. - Task 5 GREEN verification: direct MinGW compile and run of
build_task5_ir_skeleton_test.exepassed with exit code 0. - CMake is still unavailable in PATH (
cmake --versionfailed), so full Task 5 verification used direct MinGW no-libusb builds. - Full no-libusb direct verification after Task 5 passed:
test_c_api,test_tcm_frame,test_tcm_device,test_device_model, andtest_ir_skeletonall compiled and ran; output ended withALL_DIRECT_TESTS_PASSED. - Implemented Task 6
mag160c-cliwithprobe,tcm-rotate --dry-run,tcm-light --dry-run, andir-info. - Task 6 RED verification: direct MinGW compile of
tools/mag160c_cli.cppfailed as expected because the CLI source file did not exist. - Task 6 CLI verification:
build_task6_mag160c_cli.exeno-args printed usage and exited 1;tcm-rotate --dry-run 5printed7e 00 07 85 02 77 00 01 00 05 7f;tcm-light --dry-run green blinkprinted7e 00 09 87 02 32 00 01 01 00 ff 00 35;ir-infoprinted MAG160C skeleton info and known Linux endpoint evidence. - Full no-libusb direct verification after Task 6 passed again for
test_c_api,test_tcm_frame,test_tcm_device,test_device_model, andtest_ir_skeleton; output ended withALL_DIRECT_TESTS_PASSED.
2026-08-10 Deep Disassembly: Full Protocol Recovery
- Network became available; pip-installed capstone 5.0.7 + pyelftools 0.33 into
.tools/python-revlibs(verified: r2pipe/angr/keystone/unicorn also installed). Built reusableanalysis/revtools.py(ELF loader, ARM64 PLT/GOT resolver, lite C++ demangler, xref scan) and used MinGW objdump/readelf on the x86-64 Linuxlibmagcore.so.2.1.1and WindowsCoreSDKLib.dll. - Recovered the complete vendor USB protocol from libmagcore (was the #1 open question):
- Link: VID 0x833C, set_configuration(2), claim_interface(0), auto-detach on, optional pid/bus/addr filters.
- Command channel: EP OUT 0x03 + EP IN 0x82 (0x1000, 2000ms); 8-byte packets
{u32 magic, u32 param}; 0x3c-byte packets for DDT. - Command magic table: 0x6bb6b66b/66c prepare, 66d/66e DDT read/write (0x38-byte payload), 66f camera info, 670 version, 671 (Windows-only), 672 FFC, 673 start (after 50ms + thread setup), 674 stop, 676/677 set params (Linux-only).
- Response magic table: 0x5bb5b55b/55c/55d/55f = 0x38-byte info blocks (55b: pid@0, width@0x10, height@0x14), 55e = 16-byte qword pair, 57b = saved-file header.
- Frame stream EP 0x81: marker 0x1bb1b11b@0, frame counter@4, data length@8, type@0xc (0/1), shutter@0x10, pixel data@0x1c (uint16 LE), trailing 0x1bb1b11c@0x1c+len, total 0x38+len, width 160 or 384. Reader thread 0x170a4 + dispatcher 0x16fb5.
- Windows CoreSDKLib.dll cross-validates: same magic codes (66b/66c/66f/670/671/672/673/674), same frame markers with 0x1c offset, same 50ms-then-start flow, same 0xe4ae error domain, same response dispatch shape (0x5bb5b55b..0x5bb5b560).
- Recovered temperature conversion math from ARM64 libcoresdk.so exported CFunctions:
- Calibration (0x6a41c): per-pixel PWL - diff=(raw-baseline)>>1, band search on per-pixel threshold table (stride bands-1), v=off[i][p]+(diff*coeff[i][p]>>12), clamp [0,0xffff].
- ConvertResponse2Temperature (0x73124): base=(50000-gain)>>shift + diff*coeff, auto-gain decrement when >0x101 pixels saturate.
- CAccumulator::PushFrame (0x62548): baseline copy/avg accumulation with restart state machine.
- GetTemperature (0x7252c): window average (radius 1..3) -> ReviseTemperature (0x6cdb8, T2E PWL 0x112 entries + emissivity float mixing + inverse-map binary search) -> CorrectTemperature (0x6d6b0, offset segmentation k=(v-15000)/100 clamped 0..6, float gains +0xc4/+0xcc/+0xd0).
- T2E table symbol exported (
T2E@ 0x402010), monotonic 1000..737212, dumped to analysis/t2e_table.json; float constants 1000.0/0.01/0.99; units consistent with millidegree-C scale. - DDT saved file: 0x5bb5b57b header + aligned payload + 0x5bb5b55b/55c/55d/55e/55f typed blocks.
- Wrote complete spec to
analysis/protocol_spec.md; updatedfindings.mdwith the 2026-08-10 session summary. - Implementation updates (all passing direct MinGW builds, same method as before):
- include/mag160c/mag160c.h: frame header struct, all magic constants, temp-conversion constants.
- src/core/ir_frame.{hpp,cpp}: recovered frame parser (parse_ir_frame) + Calibration piecewise-linear converter (calibrate_frame).
- tests/cpp/test_ir_frame.cpp: 7 tests (frame parse valid/bad marker/bad type/bad trailer/truncated, calibration band select/interpolation+clamp/negative diff).
- ir_device.cpp + CLI ir-info now report the recovered protocol; skeleton errors changed from PROTOCOL_UNKNOWN to UNSUPPORTED; CMakeLists updated.
- Full suite green: test_c_api, test_tcm_frame, test_tcm_device, test_device_model, test_ir_skeleton, test_ir_frame all EXIT=0; mag160c-cli ir-info shows the recovered protocol line.
2026-08-10 Windows Test Prep + USB Capture Tooling
- Organized workspace: disassembly outputs moved to
analysis/disasm/, legacy C++ exe artifacts moved toanalysis/legacy-cpp/build/, temp files removed. - Hardware check: no VID 0x833C device currently attached (present USB devices: FFFF/FFFF, 0CF2/7750, 258A/010C, Logitech receiver). User will plug the camera in for Windows testing.
- Installed USB capture tooling via winget:
- USBPcap 1.5.4.0 (
desowin.USBPcap) - driver + USBPcapCMD.exe - Wireshark 4.6.7 (
WiresharkFoundation.Wireshark) - Reboot needed for the USBPcap driver to load.
- USBPcap 1.5.4.0 (
- Windows libusb-1.0 build support:
- Found a 64-bit
libusb-1.0.dllin TI CCS (C:\ti\ccs2050\ccs\ccs_base\common\bin\); openocd's copy is 32-bit (pei-i386), which crashed with 0xC000007B on a 64-bit exe. - Generated MinGW import library with
gendef+dlltool -k(kill-at so symbols match the undecorated x64 calls) intocsdk/third_party/libusb/win64/(libusb.h + libusb-1.0.dll + libusb-1.0.x64.a + original .def). - CMakeLists now falls back to this bundled libusb on Windows when pkg-config finds nothing, and copies the DLL next to the CLI.
- Found a 64-bit
- Verified full libusb+threads build on Windows:
csdk_cli_libusb.exeruns; tcm/frame/temp tests pass;ir-inforeports "no device with VID 0x833c" (libusb enumeration works);ffcreturns NOT_OPEN cleanly.
- Wrote
csdk/WINDOWS_TESTING.md: build commands, hardware test flow, USBPcap capture procedure, and the pcap verification checklist against the recovered protocol (magic bytes per command/response/frame).
2026-08-10 Pure-C SDK Rewrite (csdk/)
- User requested a C SDK faithful to the reverse-engineered protocol; the
previous C++ implementation (fake
ir_open, dynamic endpoint discovery, missing temperature math) was replaced. Old tree archived toanalysis/legacy-cpp/. - New pure-C tree in
csdk/:include/mag160c/mag160c.h- C ABI: vendor error domain, all magic command/response codes, frame format constants, temperature pipeline API, TCM framing API.src/mag160c_error.c- TLS last-error.src/mag160c_frame.c- 0x1bb1b11b frame parse + streaming assembler (marker align, length check, trailing marker, 0x38+len consumption).src/mag160c_temp.c- Calibration (per-pixel PWL w/ baseline), ConvertResponse2Temperature (auto-gain + saturation counting), T2E Q13 interpolation using the dumped tables.src/mag160c_tcm.c- FTDICommand 0x7e framing; NOTE: checksum is additive mod-256 (0x7e+0x07=0x85), byte-exact vs. the old C++ tests.src/mag160c_ir.c- libusb session: VID 0x833C, config 2, iface 0, EP 0x03/0x82 command channel, EP 0x81 frame reader thread, 50 ms then START, STOP, FFC; compiles in libusb/threads/no-libusb modes.src/mag160c_tables.h- T2E + E2TAccQ10 tables generated from the ARM64 binary dump.tools/mag160c_cli.c- ir-info / ffc / start / stop / tcm-rotate / tcm-light / frame-test / temp-test.tests/- test_frame, test_temp, test_tcm, test_api.
- Verification (mingw gcc, no-libusb mode): all 4 tests pass (ALL_CSDK_TESTS_PASSED); threads mode passes; libusb+threads mode compiles clean (no libusb .lib on this machine to link).
- CLI output verified:
tcm-rotate 5->7e 00 07 85 02 77 00 01 00 05 7f(byte-exact),tcm-light green blink->7e 00 09 87 02 32 00 01 01 00 ff 00 35, frame-test/temp-test pass, ir-info prints the recovered protocol.
2026-07-14 Workspace Organization
- Organized generated and analysis files without deleting source or vendor SDK material.
- Moved root
build_*.exebinaries intobuild-artifacts/. - Moved
.reverse/toanalysis/reverse-cache/. - Moved
reverse_tools/toanalysis/reverse_tools/. - Moved root
【模块文档】MAG160CORE.pdftovendor-docs/【模块文档】MAG160CORE.pdf. - Left vendor SDK/app directories and active source directories in place:
IR_Camera_SDK-1.0.1/,app/,src/,include/,tests/,tools/, anddocs/.
2026-08-10 Live Hardware Protocol Verification (WinUSB/libusb-win32)
User connected the actual device (Magnity Thermal Camera, VID 0x833C PID 0x0001, serial 160043865). Full protocol verified live against the official Windows demo (EloThermal.exe, which shows a real thermal image).
Driver / libusb matrix (important)
- Device ships without a driver; SDK's ThermoCube_stream.inf fails signature validation on Win10/11 (2012 cat). Zadig installed WinUSB, then libusb-win32 for the official demo (demo links libusb0.dll directly).
- Working combo: TI libusb-1.0.dll + libusb-win32 driver (all commands/stream work; harmless "monotonic clock" warnings). Official libusb-1.0.27 MinGW build crashes on bulk transfer with the libusb-win32 driver; official DLL + WinUSB crashes in libusb_control_transfer; TI DLL + WinUSB fails FFC writes.
Protocol verified on hardware (differs from Linux-SDK assumptions)
- Commands are 4-byte packets (magic only); only FFC (0x6bb6b672) uses the 8-byte {magic, param} form.
- Init: 66b(4B) -> 66c(4B) -> 66f(4B). Response 0x5bb5b55b from 66b carries the camera info (width@+0x10=160, height@+0x14=120, fps@+0x18=15). The 0x5bb5b55c block (from 66c) is a DIFFERENT layout and must not be parsed as info.
- START: FFC(0) x2, sleep 300ms, then 73(4B). This switches the unit into live imaging mode (frames with type field 0/1). FFC(param=1) toggles type 1->0, FFC(param=0) toggles 0->1; the official demo alternates FFC every ~10 frames.
- STOP: clear_halt(0x03/0x82) then 74(4B) works.
- Frame stream: 28-byte header (marker 0x1bb1b11b, counter, len=0x9600, type, shutter) + 38400-byte payload + 28-byte tail (0x1bb1b11c trailer), at 15 fps. Verified: 4 s capture = 60 frames.
- Thermal response: hand vs background delta ~1322 counts (whole-FOV hand) => roughly 147 counts/C with a plausible 34C vs 25C assumption.
csdk fixes made from live testing
- send_cmd4 for 66b/66c/66f/73/74; FFC stays 8-byte with param.
- ir_open sequence 66b,66c,66f (was sending 670 in third slot).
- FFC(0) x2 before START in ir_start.
- Keep libusb_context alive for handle lifetime (was libusb_exit'ing it).
- Only 0x5bb5b55b parsed as info (0x5bb5b55c corrupted width/height).
- GUI live thermal viewer (thermal_viewer.c) shows real-time pseudo-color imagery; single-threaded read/render loop required (TI libusb DLL is unreliable with a reader thread + GUI thread).
Artifacts
- official demo traffic capture: analysis/captures/libusb0_trace.txt (26k lines: init, periodic FFC, 10k+ frames) via a libusb0.dll API shim.
- ETW capture experiments (USB-UCX has no payload data; etl2pcapng is network-only) - kept as notes in csdk/WINDOWS_TESTING.md.
2026-08-10 Post-reboot USBPcap Verification
- System rebooted; USBPcap driver now bound to the root hubs. Device is on \\.\USBPcap4 (AMD USB 3.x hub on this machine).
- Captured the csdk's own live traffic with USBPcap (no shim needed anymore): analysis/captures/csdk_verified.pcap.
- Sequence verified identical to the official demo: 66b -> 66c -> 66f -> FFC(0) x2 -> START -> 61 frames @ 15 fps -> STOP.
- csdk frame callback test: 60 frames in 4 s (type=1). All tests green.
2026-08-10 (evening)
- Task 1: researched open-source thermal display pipelines (OpenThermal/libseek-thermal SeekCam.cpp, MLX90640 API, FLIR Lepton AGC/LUT). Key learnings: bad pixel = histogram peak-deviation threshold (thr = peak - (max - peak)) + temporal min/max, topological 4-neighbour fill; AGC = percentile stretch + ironbow LUT; diff AGC = adaptive mean-based span.
- Task 2: rewrote tools/mag160c_demo2.c v3: Seek-style bad pixel detection + topological fill order applied to live frame and reference; adaptive diff AGC (span=2*mean|d|, deadband); ironbow LUT; two-point calibration buttons (Cal Cold 0C / Cal Hot 90C / Reset).
- Task 3: decoded official FFC cadence from libusb0_trace.txt (26092 lines): FFC(1) after ~frame 10, then FFC(0) -> 9 frames -> FFC(1) -> variable gap (34..2680). Built mag160c_ffc_test.c with period 400 + gap 9: PASS 1400 type=0 frames / 95s / 15.1 fps / 0 stalls (twice). 44-frame period failed (fps drop + 17.7% type=1).
- Task 4: added two-point linear calibration (mag160c_temp_calibrate_linear) to csdk + demo2 GUI buttons; physical measurement (ice water / boiling water) still TODO by user.
- Task 5: new csdk module src/mag160c_display.c + include/mag160c/mag160c_display.h (badmap, AGC range/diff span, ironbow, FFC scheduler, linear cal); mag160c_ir.c now supports mag160c_ir_set_ffc_scheduler() ticking in the reader thread. test_display.c added (20/20 pass); all old tests still pass; CLI gained display-test/ffc-test/calibrate.
- Hardware validation: csdk_stream_test.exe (threaded reader + scheduler) PASS 1030 type=0 frames / 14.7 fps; csdk_cli_libusb.exe ffc-test 1000 PASS. demo2 GUI verified rendering type=0 with bad-pixel red/blue down to ~0.02% and no saturated defects.
- Evidence: analysis/demo2_v3_verified_20260810.png (GUI screenshot).
2026-08-10 (late night, third fix round)
- User reported: FFC flash fixed but (1) startup noise image (red/yellow dots) persists and overlays the normal view, (2) ghosting still present.
- Root cause: MOG permanent-freeze defect - reference collected before the first auto-FFC cycle baked startup noise in; frozen pixels (|d|>=60) could never heal, so noise + wrong reference persisted forever.
- Fixes in mag160c_demo2.c:
- first reference collection moved to right after startup FFC(1) settling (nread>=40, not >=150)
- reference re-collected after EVERY FFC(1) (quiet-frame median, keeps bad map) instead of one-shot median rebase - any stale reference self-heals within one FFC cycle (~27s)
- MOG self-healing: isolated foreground pixels (8-neighbor fg < 2) stuck for >=30 frames are reference errors (startup noise/bad pixel), reset to live value; contiguous objects never healed/absorbed
- csdk: new mag160c_display_ref_track_heal() (w,h variant with fg counters); demo2 uses it directly. test_display now 30/30.
- Verified on hardware: startup noise 0.12% -> 0.10% over 2157 frames (5+ FFC cycles), no growth, no overlay; red content all real scene blobs; no FFC flash (red stable, blue 0.01%); stream never stalls.
- Evidence: analysis/demo2_v4_selfheal_verified_20260810.png
2026-08-10 (late night, fourth fix round - root cause found)
- User reported startup noise image and ghosting STILL present after the MOG self-heal fix. Started a data-driven investigation instead of more reference-model tweaks.
- Captured 60 real type=0 frames (tools/mag160c_frame_dump.c) and analyzed:
- temporal noise std ~248 counts per pixel (99.9% of pixels) -> diff mode with adaptive span ~400 amplifies this into visible red/blue speckle = the "startup noise image"
- fixed sensor mura: row profile span 15594 (row1 constant +5000), col span 8714, FFT shows ~10% energy on row/col axes
- mura drifts ~350 counts per session and jumps after FFC -> reference diff shows old pattern = ghosting
- flat-field correction live-(ref-mean(ref)) reduces mura std 3557->379
- Decided to abandon the scene-diff approach (which caused both problems) and adopt the official pipeline: raw -> NUC flat-field correction -> absolute temperature display (median-centered window + ironbow) + 3x3 median smoothing (cuts temporal noise ~3x).
- csdk: new mag160c_display_nuc(); reference capture now rejects frames where >5% pixels deviate from the running average (persistent objects never enter the flat field) - the anti-ghost guarantee. diff mode kept as optional.
- Verified on hardware: speckle residual 2.2-2.7 (smooth), pure blue 0%, red content all real scene blobs (1-1.4%), 5+ FFC cycles with no flash and no pattern growth, stream 2268+ frames. csdk tests 32/32.
- Evidence: analysis/demo2_v5_nuc_verified_20260810.png