完成官方管线全量逆向与 demo3 v5 复刻,清理仓库

- 逆向:Ghidra/IDA 全量反编译 CoreSDKLib.dll/ThermalSDK.dll/libthermalSDK.so/
  libcoresdk.so(ARM64)/libmagcore.so,导出 analysis/ida/export/
- 解码官方渲染管线:DDT 校准表加载->快门端点选择->Q12 插值->ref(4x type1 帧
  均值)->NUC 查表->盲元补偿->窗口->LUT1024 重建->2x 升采样->调色板
- 逐像素验证:NUC+盲元 0/19200、插值 0 误差、2x 0/76800、窗口一致
- demo3 v5:完整复刻官方管线(含 DDT 解析、FFC 状态机、快门温度驱动),
  修复 load_ddt 表错位导致的零像素问题
- 鬼影根因分析写入 analysis/reverse_20260813_full.md
- 心跳/恢复机制:analysis/session_state.md + tools/resume_rev.ps1
- 新增 tsdk_pair3 增强采集工具;历史工具归档 csdk/tools/legacy/;
  根目录抓帧残留删除,历史文档归档 analysis/history/
- csdk/README.md 完整使用文档;.gitignore/.gitattributes 补 LFS 规则
This commit is contained in:
ZXCLI
2026-08-13 23:16:12 +08:00
parent 9739a1972c
commit 0bfb926892
622 changed files with 864390 additions and 2433 deletions
+208 -38
View File
@@ -1,55 +1,225 @@
# MAG160C C SDK (csdk/)
Pure C reimplementation of the MAG160C thermal camera SDK, written from the
recovered protocol (see `analysis/protocol_spec.md` for the full reverse
engineering report). The previous C++ implementation was archived to
`analysis/legacy-cpp/`; this tree replaces it.
MAG160C 热像仪(160×120USB VID 0x833C)的纯 C 重新实现,基于对原厂
Windows/Linux/Android SDK 的完整逆向(见 `../analysis/reverse_20260813_full.md`)。
包含:USB 会话层、帧解析/流组装、温度换算、TCM 板通信、以及**官方渲染管线的
逐像素复刻**demo3 v5)。
## Layout
## 目录结构
```
csdk/
include/mag160c/mag160c.h public C ABI (error domain, magic codes, frame
format, temperature API, TCM framing)
include/mag160c/
mag160c.h 公共 C ABI(错误域、协议常量、帧格式、温度、TCM)
mag160c_display.h FFC 调度器 + 显示辅助(ref rebase 等)
src/
mag160c_internal.h shared helpers (last-error)
mag160c_error.c TLS last-error + error names
mag160c_frame.c 0x1bb1b11b frame parse + streaming assembler
mag160c_temp.c Calibration / ConvertResponse2Temperature / T2E
mag160c_tcm.c FTDICommand 0x7e framing + rotate/light builders
mag160c_ir.c libusb session (link/start/stop/ffc/info)
mag160c_tables.h T2E + E2TAccQ10 tables (generated from binary)
tools/mag160c_cli.c CLI: ir-info, ffc, start, stop, tcm-rotate,
tcm-light, frame-test, temp-test
tests/ test_frame, test_temp, test_tcm, test_api
mag160c_internal.h 内部共享(last-error
mag160c_error.c TLS last-error + 错误名
mag160c_frame.c 0x1bb1b11b 帧解析 + 流组装器
mag160c_temp.c Calibration / ConvertResponse2Temperature / T2E
mag160c_tcm.c FTDICommand 0x7e 组帧 + 旋转/指示灯构建
mag160c_ir.c libusb 会话(link/start/stop/ffc/info/回调/FFC调度)
mag160c_display.c 显示辅助(FFC 调度器、ref rebase、坏点)
mag160c_tables.h T2E/E2TAccQ10 温度表(从二进制提取)
mag160c_official_*.h 官方调色板 / LUT1024 / T2E(从官方活体抓取导出)
tools/
mag160c_demo3.c 主程序:官方管线复刻(DDT+NUC+盲元+窗口+LUT+2x
tsdk_pair2.c 官方 ThermalSDK 同帧抓帧工具(含表/盲元/快门)
tsdk_pair3.c 增强采集(DDT 复制、完整端点表、盲元、h1024/cdf)
mag160c_cli.c CLIir-info/ffc/start/stop/tcm-rotate/tcm-light 等
mag160c_csdk_stream_test.c csdk 流测试
legacy/ 历史探索工具(归档,勿用)
tests/ test_frame / test_temp / test_tcm / test_api / test_display
CMakeLists.txt CMake 构建(可选;当前环境直接用 gcc)
WINDOWS_TESTING.md Windows 硬件测试指南(USBPcap/Wireshark/MinGW
```
## Build
## 构建
### 方式 A:直接 GCC(当前 Windows 环境)
```powershell
# CLI 工具
gcc -O2 -w -DMAG160C_STATIC -I"csdk\third_party\libusb\win64" -I"csdk\include" -I"csdk\src" `
-o build-artifacts\mag160c_cli.exe csdk\tools\mag160c_cli.c `
csdk\src\mag160c_ir.c csdk\src\mag160c_frame.c csdk\src\mag160c_temp.c `
csdk\src\mag160c_tcm.c csdk\src\mag160c_error.c csdk\src\mag160c_display.c `
csdk\third_party\libusb\win64\libusb-1.0.x64.a
# 主程序 demo3(官方管线复刻)
gcc -O2 -w -DMAG160C_STATIC -I"csdk\third_party\libusb\win64" -I"csdk\include" -I"csdk\src" `
-o build-artifacts\mag160c_demo3.exe csdk\tools\mag160c_demo3.c `
csdk\src\mag160c_display.c csdk\src\mag160c_error.c `
csdk\third_party\libusb\win64\libusb-1.0.x64.a -lgdi32 -luser32
# 官方抓帧工具
gcc -O2 -w -o build-artifacts\tsdk_pair3.exe csdk\tools\tsdk_pair3.c
```
cmake -B build -S csdk # auto-detects libusb-1.0 via pkg-config
libusb 依赖:`csdk/third_party/libusb/win64/`(头文件 + DLL + MinGW 导入库)。
### 方式 BCMake(有 CMake 时)
```powershell
cmake -B build -S csdk
cmake --build build
ctest --test-dir build
```
Without libusb-1.0 the IR session APIs return MAG160C_ERR_NOT_SUPPORTED;
the pure helpers (frame parse, temperature math, TCM framing) and their tests
work with no dependencies. Threads (pthread) are used when available.
libusb-1.0 时,IR 会话 API 返回 `MAG160C_ERR_NOT_SUPPORTED`;纯计算
(帧解析、温度、TCM)和对应测试无依赖可跑。
## Recovered protocol summary
## 快速开始
- VID 0x833C, config value 2, interface 0.
- Commands: EP OUT 0x03, 8-byte packets `{u32 magic, u32 param}` (0x3c-byte
with 0x38-byte payload for DDT). Response: EP IN 0x82 (0x1000 max, 2000 ms).
- Magic commands: 0x6bb6b66b/66c prepare, 66d/66e DDT, 66f info, 670 version,
672 FFC, 673 start, 674 stop, 676/677 set params.
- Magic responses: 0x5bb5b55b..55f (0x38-byte info blocks / 0x10 pair),
0x5bb5b57b file header.
- Frame stream: EP IN 0x81, marker 0x1bb1b11b @0, counter @4, data length @8,
type @0xc (0 response / 1 raw), shutter @0x10, pixels @0x1c (uint16 LE),
trailing 0x1bb1b11c @0x1c+len, frame size 0x38+len.
- Temperature: per-pixel piecewise-linear Calibration with baseline,
ConvertResponse2Temperature auto-gain, T2E curve (0x112 entries) with Q13
band interpolation.
- TCM: FTDICommand 0x7e framing with additive (mod-256) checksums; command
table main 0x02 (rotate 0x77, light 0x31/32/33, ...).
### 1. 连接并启动(核心 API 流程)
```c
#include <mag160c/mag160c.h>
#include <mag160c/mag160c_display.h>
static void on_frame(uint32_t idx, const uint8_t *data, size_t len, void *user) {
/* data: 完整帧(0x1c 头 + 像素)。用 mag160c_frame_parse 解析。 */
mag160c_frame_header_t hdr;
const uint16_t *pixels;
if (mag160c_frame_parse(data, len, &hdr, &pixels) == MAG160C_OK && hdr.frame_type == 0)
; /* pixels[19200] 小端 uint16160×120 */
}
int main(void) {
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
mag160c_init(&ctx);
mag160c_ir_open(ctx, &ir); /* 自动找 VID 0x833cconfig 2interface 0 */
mag160c_ir_prepare(ir); /* 66b/66c/66f + FFC(0)x2 + 300ms */
mag160c_ir_set_frame_callback(ir, on_frame, NULL);
/* 官方 FFC 节律(周期 1800 帧 / 间隔 9),保持 type=0 流稳定 */
static mag160c_ffc_scheduler_t sched;
mag160c_ffc_scheduler_init(&sched, 1800, 9);
mag160c_ir_set_ffc_scheduler(ir, &sched, 0);
mag160c_ir_start(ir); /* 线程收帧 + 0x6bb6b673 */
/* ... 收帧 / 读温度 ... */
int32_t temp_mc = 0;
mag160c_ir_read_temperature(ir, 80, 60, &temp_mc);
mag160c_ir_stop(ir);
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return 0;
}
```
### 2. 关键 API
| API | 说明 |
|---|---|
| `mag160c_ir_open/close` | USB 设备会话 |
| `mag160c_ir_prepare` | 官方初始化命令序列 |
| `mag160c_ir_start/stop` | 起停流(0x6bb6b673 / 0x6bb6b674|
| `mag160c_ir_trigger_ffc(param)` | 手动 FFC0 或 1|
| `mag160c_ir_set_ffc_scheduler` | 挂官方节律 FFC 调度器 |
| `mag160c_ir_set_frame_callback` | 每帧回调(解析后数据)|
| `mag160c_ir_read_temperature(x,y,&t)` | 探针温度(毫摄氏度×100|
| `mag160c_frame_parse` | 纯解析:marker/计数/类型/像素指针 |
| `mag160c_frame_stream_*` | 流组装器(乱序/分包恢复)|
| `mag160c_temp_calibrate` | 官方逐像素分段线性校准(NUC 温度域)|
| `mag160c_temp_convert_response` | 自动增益线性换算 |
| `mag160c_temp_t2e_interp` | T2E 曲线 Q13 插值 |
| `mag160c_tcm_*` | TCM 板 FTDICommand 0x7e 帧(旋转/指示灯)|
错误码域 `mag160c_error_t` 与官方 errno 域 `0xE4AE0001..` 对齐;
`mag160c_last_error()` 取线程局部最近错误字符串。
### 3. CLI 用法(mag160c_cli.exe,无硬件可跑纯计算)
```powershell
build-artifacts\mag160c_cli.exe ir-info # 枚举/信息
build-artifacts\mag160c_cli.exe frame-test # 帧解析自测
build-artifacts\mag160c_cli.exe temp-test # 温度换算自测
build-artifacts\mag160c_cli.exe tcm-rotate 5 # TCM 旋转指令编码
build-artifacts\mag160c_cli.exe tcm-light green blink # TCM 指示灯指令编码
```
### 4. 主程序 demo3(官方渲染管线复刻)
`tools/mag160c_demo3.c` 是当前维护的 Windows 直连演示/研究程序,逐像素复刻
CoreSDKLib.dll 的完整链路:
```
USB帧(type=0) → 快门提取(帧尾字) → FFC状态机(0x9ee0)
→ 端点选择+Q12插值(0x16ae0/0x16dd0/0x16f10, 快门驱动)
→ ref = FFC窗口4个type=1帧均值(0x41ee0 mode=4)
→ NUC查表(0x17200) → 盲元补偿(0x17330, 32条固定记录)
→ 统计+窗口(0x10780/0x109c0, half=312) → LUT1024重建(0x11ee0)
→ 灰度+2x升采样(0x19740) → 官方调色板
```
运行(需要 `build-artifacts/` 下的 DDT 表文件):
```powershell
Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue
Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDirectory "C:\Project\MAG160C\build-artifacts"
```
依赖文件(放 exe 同目录):
| 文件 | 来源 |
|---|---|
| `mag160c_official.ddt` | 官方 DDT 校准文件(`%TEMP%\Core<序列号>` 副本)|
| `mag160c_official_nuc_gain.bin` | 官方 NUC gain/off 表(历史抓取,备用)|
| `mag160c_official_nuc_thr.bin` | 官方 NUC 阈值表(历史抓取,备用)|
| `libusb-1.0.dll` | libusb 运行库 |
验证状态(2026-08-13):
- NUC+盲元 与官方同帧抓帧逐像素 0/19200 误差
- 阈值/gain 插值 0 误差;2x 升采样 0/76800 误差;窗口公式与官方一致
- 同静态场景 gray 直方图与官方相关性 0.92(跨会话)
- 输出日志:`demo3_diag.txt`(每帧统计)、`demo3_ffc.txt`FFC 事件)、
`demo3_auto_*.bmp`(每 30 帧快照)
### 5. 官方抓帧工具(tsdk_pair2 / tsdk_pair3
用途:对照验证 demo3 与官方管线。先停 demo3,再运行(同一设备独占):
```powershell
# tsdk_pair2:官方 ThermalSDK 同帧抓帧(gray/rgb/raw/ref/f20/tables/win
& build-artifacts\tsdk_pair2_fixed.exe C:\Project\MAG160C\analysis\pairs_xxx 12 6000
# tsdk_pair3:增强采集(+ 完整端点表/盲元记录/shutter/h1024/cdf/DDT 复制)
& build-artifacts\tsdk_pair3.exe C:\Project\MAG160C\analysis\pairs_xxx 8 6000
# 注意:config.txt 和 pair_*.meta/win 写在进程 CWD,跑完移回输出目录
```
参数:`<输出目录> <抓帧对数> <官方SDK初始化等待ms>`
若报 "no channel":停止所有程序后重新插拔 USB。
## 测试
```powershell
# 无硬件验证(纯计算)
build-artifacts\csdk_test_frame.exe
build-artifacts\csdk_test_temp.exe
build-artifacts\csdk_test_tcm.exe
build-artifacts\csdk_test_api.exe
build-artifacts\csdk_test_display.exe
```
## 协议要点(详见 analysis/protocol_spec.md
- 端点:EP0x03 命令 OUT / 0x82 响应 IN / 0x81 帧流 IN / 0x84 大块读 IN
- 命令 4 字节 `{magic}`,仅 FFC(0x6bb6b672) 用 8 字节 `{magic, param}`
- 帧:`0x1bb1b11b | counter | len | type(0/1) | ... | 像素 u16 LE | 0x1bb1b11c`
- 快门(设备温度代理)= 帧尾字 `frame[len+0x24]`width=160 时)
- 温度:`temp_mC = T2E[3*nuc - C]`C≈5797(会话相关)
## 文档索引
| 文档 | 内容 |
|---|---|
| `../analysis/protocol_spec.md` | USB 协议/帧/命令/温度完整规格 |
| `../analysis/reverse_20260813_full.md` | 官方管线 Ghidra 全量逆向 + 鬼影根因 |
| `../analysis/session_state.md` | 当前会话状态/恢复点(心跳)|
| `../交接_完整逆向工程说明_20260811.md` | 项目总交接(目录/环境/历史)|
| `WINDOWS_TESTING.md` | Windows 抓包与硬件测试指南 |
| `legacy/` | 历史工具源码归档(早期探索,功能已被现工具取代)|
+725 -1107
View File
File diff suppressed because it is too large Load Diff
+316
View File
@@ -0,0 +1,316 @@
/* tsdk_pair3: same-frame capture of official pipeline outputs + full NUC/
* window/ref state, for replicating CoreSDKLib.dll in the C SDK.
* Extends tsdk_pair2 with:
* - per-frame shutter (metadata+0x2c), frame counters
* - config dump: 0x24, 0x48, 0x4c, 0x54/0x58/0x5c/0x60, 0x47944..0x47954,
* 0x11c, DDT header fields (0x41340..0x41880), endpoint arrays
* - DDT file copy (path from dev+0x4134c)
* - stats (0x42010..0x42024) + window (0x4202c/0x42030) + LUT1024 + hist
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef void (CALLBACK *fn_SetTempBoundary_t)(double, double, double);
typedef void (CALLBACK *fn_SetUnitMode_t)(int);
typedef void (CALLBACK *fn_ReadTemperatureAtPoint_t)(int, int, unsigned char *);
typedef int (CALLBACK *MAG_GetOutputBMPdata_t)(int, unsigned char **, void **);
typedef int (CALLBACK *MAG_GetOutputBMPdataRGB24_t)(int, unsigned char *, int, int);
typedef int (CALLBACK *MAG_GetTemperatureData_Raw_t)(int, unsigned int *, int, int);
typedef int (CALLBACK *MAG_GetOutputColorBardata_t)(int, void **, void **);
typedef int (CALLBACK *MAG_TriggerFFC_t)(int, int);
static volatile int g_frames;
static unsigned char g_cb[320 * 240 * 3];
static int g_cbw, g_cbh;
static const char *g_dir = ".";
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
if (buf && w > 0 && h > 0 && ch == 3) {
memcpy(g_cb, buf, (size_t)w * h * ch);
g_cbw = w; g_cbh = h;
}
g_frames++;
}
static void save(const char *name, const void *buf, size_t n) {
char path[512];
snprintf(path, sizeof(path), "%s\\%s", g_dir, name);
FILE *f = fopen(path, "wb");
if (f) { fwrite(buf, 1, n, f); fclose(f); }
}
static int is_ro_ptr(const void *p, size_t n) {
if (!p) return 0;
MEMORY_BASIC_INFORMATION mi;
if (VirtualQuery(p, &mi, sizeof(mi)) == 0) return 0;
if (mi.State != MEM_COMMIT) return 0;
if (mi.Protect == PAGE_NOACCESS) return 0;
if ((size_t)((const char *)p - (const char *)mi.BaseAddress) + n > mi.RegionSize)
return 0;
return 1;
}
int main(int argc, char **argv) {
int npairs = 24;
int start_delay = 6000;
double tb[3] = {30.0, 44.0, 37.0};
if (argc > 1) g_dir = argv[1];
if (argc > 2) npairs = atoi(argv[2]);
if (argc > 3) start_delay = atoi(argv[3]);
if (argc > 4) tb[0] = atof(argv[4]);
if (argc > 5) tb[1] = atof(argv[5]);
if (argc > 6) tb[2] = atof(argv[6]);
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE t = LoadLibraryA("ThermalSDK.dll");
HMODULE c = LoadLibraryA("CoreSDKLib.dll");
printf("T=%p C=%p\n", (void*)t, (void*)c);
if (!t || !c) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(t, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(t, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(t, "SetNewIRFrameDelegate");
fn_SetTempBoundary_t T_Bound = (fn_SetTempBoundary_t)GetProcAddress(t, "SetTempBoundary");
fn_SetUnitMode_t T_Unit = (fn_SetUnitMode_t)GetProcAddress(t, "SetUnitMode");
fn_ReadTemperatureAtPoint_t T_Temp = (fn_ReadTemperatureAtPoint_t)GetProcAddress(t, "ReadTemperatureAtPoint");
MAG_GetOutputBMPdata_t G_Gray = (MAG_GetOutputBMPdata_t)GetProcAddress(c, "MAG_GetOutputBMPdata");
MAG_GetOutputBMPdataRGB24_t G_RGB = (MAG_GetOutputBMPdataRGB24_t)GetProcAddress(c, "MAG_GetOutputBMPdataRGB24");
MAG_GetTemperatureData_Raw_t G_TempRaw = (MAG_GetTemperatureData_Raw_t)GetProcAddress(c, "MAG_GetTemperatureData_Raw");
MAG_GetOutputColorBardata_t G_Bar = (MAG_GetOutputColorBardata_t)GetProcAddress(c, "MAG_GetOutputColorBardata");
if (!T_Start || !T_Set || !G_Gray || !G_RGB) { printf("missing export\n"); return 2; }
if (T_Unit) T_Unit(0);
if (T_Bound) T_Bound(tb[0], tb[1], tb[2]);
T_Set(onIR);
printf("Start()...\n"); fflush(stdout);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
printf("warming %d ms...\n", start_delay); fflush(stdout);
Sleep(start_delay);
unsigned char *gray = NULL;
void *hdr = NULL;
int chan = -1;
for (int i = 0; i < 8; ++i) {
int rc = G_Gray(i, &gray, &hdr);
printf("chan %d: rc=%d gray=%p hdr=%p\n", i, rc, (void*)gray, (void*)hdr);
if (rc && gray && hdr) { chan = i; break; }
}
if (chan < 0) { printf("no channel\n"); T_Stop(); return 3; }
unsigned char *dev = (unsigned char *)hdr - 0xaf0;
int w = *(int *)(dev + 0xaf4);
int h = *(int *)(dev + 0xaf8);
int n = w * h;
int nw = w / 2, nh = h / 2;
int nn = nw * nh;
printf("dev=%p w=%d h=%d\n", (void*)dev, w, h);
/* ---- config dump ---- */
FILE *cfg = fopen("config.txt", "w");
if (cfg) {
fprintf(cfg, "dev24=%d dev48=%d dev4c=%d dev54=%d dev58=%d dev5c=%d dev60=%d\n",
*(int *)(dev + 0x24), *(int *)(dev + 0x48), *(int *)(dev + 0x4c),
*(int *)(dev + 0x54), *(int *)(dev + 0x58), *(int *)(dev + 0x5c),
*(int *)(dev + 0x60));
fprintf(cfg, "11c=%d 41340=%d 41344=%d 41348=%d 4134c=%ls\n",
*(int *)(dev + 0x11c), *(int *)(dev + 0x41340), *(int *)(dev + 0x41344),
*(int *)(dev + 0x41348), (wchar_t *)(dev + 0x4134c));
fprintf(cfg, "41558=%d 41554=%d 4155c=%d 41560=%d 47944=%d 47948=%d 4794c=%d 47950=%d 47954=%d 47938=%d\n",
*(int *)(dev + 0x41558), *(int *)(dev + 0x41554), *(int *)(dev + 0x4155c),
*(int *)(dev + 0x41560), *(int *)(dev + 0x47944), *(int *)(dev + 0x47948),
*(int *)(dev + 0x4794c), *(int *)(dev + 0x47950), *(int *)(dev + 0x47954),
*(int *)(dev + 0x47938));
int cnt = *(int *)(dev + 0x41558);
if (cnt < 1 || cnt > 64) cnt = 1;
fprintf(cfg, "T:");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x41564 + i * 4));
fprintf(cfg, "\nB:");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x415b4 + i * 4));
fprintf(cfg, "\nC:");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x41604 + i * 4));
fprintf(cfg, "\nD(blind cnt):");
for (int i = 0; i < cnt - 1; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x41654 + i * 4));
fprintf(cfg, "\nloaded:");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x416b8 + i * 4));
fprintf(cfg, "\noffsets(41708):");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x41708 + i * 4));
fprintf(cfg, "\nthr_off(41758):");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x41758 + i * 4));
fprintf(cfg, "\nblind_off(417a8):");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x417a8 + i * 4));
fprintf(cfg, "\nblind_off2(417f4):");
for (int i = 0; i < cnt; ++i) fprintf(cfg, " %d", *(int *)(dev + 0x417f4 + i * 4));
fprintf(cfg, "\nbases: 416a0=%p 416a8=%p 416b0=%p\n",
*(void **)(dev + 0x416a0), *(void **)(dev + 0x416a8), *(void **)(dev + 0x416b0));
fprintf(cfg, "41840=%d 41848=%p 41850=%p 41858=%p 41860=%p 41868=%p 41870=%p 41878=%p\n",
*(int *)(dev + 0x41840), *(void **)(dev + 0x41848), *(void **)(dev + 0x41850),
*(void **)(dev + 0x41858), *(void **)(dev + 0x41860), *(void **)(dev + 0x41868),
*(void **)(dev + 0x41870), *(void **)(dev + 0x41878));
fprintf(cfg, "41f0c=%d 41f3c=%d 41fdc=%d 41fe0=%d 41fe4=%d 41fe8=%d\n",
*(int *)(dev + 0x41f0c), *(int *)(dev + 0x41f3c), *(int *)(dev + 0x41fdc),
*(int *)(dev + 0x41fe0), *(int *)(dev + 0x41fe4), *(int *)(dev + 0x41fe8));
fprintf(cfg, "2d47=%d 2d86=%d 2d78=%d 2d88=%d 2d8c=%d 2d90=%d 2d94=%d 2d98=%d 2d9c=%d\n",
*(char *)(dev + 0x2d47), *(short *)(dev + 0x2d86),
*(int *)(dev + 0x2d78), *(int *)(dev + 0x2d88), *(int *)(dev + 0x2d8c),
*(int *)(dev + 0x2d90), *(int *)(dev + 0x2d94), *(int *)(dev + 0x2d98),
*(int *)(dev + 0x2d9c));
fprintf(cfg, "47940=%d 44c50=%d 44c60=%d 44c64=%d 44c68=%d 42010=%d 42014=%d\n",
*(int *)(dev + 0x47940), *(int *)(dev + 0x44c50), *(int *)(dev + 0x44c60),
*(int *)(dev + 0x44c64), *(int *)(dev + 0x44c68),
*(int *)(dev + 0x42010), *(int *)(dev + 0x42014));
fclose(cfg);
}
/* copy the DDT file */
wchar_t *ddtp = (wchar_t *)(dev + 0x4134c);
if (ddtp && *ddtp) {
printf("DDT path: %ls\n", ddtp); fflush(stdout);
char apath[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, ddtp, -1, apath, MAX_PATH, NULL, NULL);
FILE *sf = fopen(apath, "rb");
if (sf) {
fseek(sf, 0, SEEK_END);
long sz = ftell(sf);
fseek(sf, 0, SEEK_SET);
void *buf = malloc(sz);
if (buf) {
fread(buf, 1, sz, sf);
save("official.ddt", buf, sz);
printf("DDT copied: %ld bytes\n", sz);
free(buf);
}
fclose(sf);
} else printf("DDT file not readable: %s\n", apath);
} else printf("no DDT path\n");
fflush(stdout);
unsigned char *rgb = malloc((size_t)n * 3);
unsigned short *raw = malloc((size_t)nn * 2);
unsigned char *pal = malloc(256 * 4);
unsigned int *t32 = malloc((size_t)n * 4);
unsigned char *g2 = malloc((size_t)n);
MAG_TriggerFFC_t G_FFC = (MAG_TriggerFFC_t)GetProcAddress(c, "MAG_TriggerFFC");
int prev = g_frames;
for (int k = 0; k < npairs; ++k) {
if (G_FFC && k == npairs / 3) { G_FFC(chan, 1); Sleep(1500); prev = g_frames; }
if (G_FFC && k == 2 * npairs / 3) { G_FFC(chan, 0); Sleep(1500); prev = g_frames; }
int t0 = GetTickCount();
while (g_frames == prev && GetTickCount() - t0 < 3000) Sleep(5);
prev = g_frames;
Sleep(2);
memcpy(g2, gray, (size_t)n);
int rcr = G_RGB(chan, rgb, n * 3, 1);
unsigned short *rawp = *(unsigned short **)(dev + 0x270);
memcpy(raw, rawp, (size_t)nn * 2);
memcpy(pal, dev + 0xb18, 256 * 4);
int rct = G_TempRaw ? G_TempRaw(chan, t32, nn * 4, 1) : 0;
char base[64];
snprintf(base, sizeof(base), "pair_%03d", k);
char nm[128];
snprintf(nm, sizeof(nm), "%s.gray", base); save(nm, g2, (size_t)n);
snprintf(nm, sizeof(nm), "%s.rgb", base); save(nm, rgb, (size_t)n * 3);
snprintf(nm, sizeof(nm), "%s.raw", base); save(nm, raw, (size_t)nn * 2);
snprintf(nm, sizeof(nm), "%s.pal", base); save(nm, pal, 256 * 4);
snprintf(nm, sizeof(nm), "%s.cbrgb", base); save(nm, g_cb, (size_t)g_cbw * g_cbh * 3);
if (rct) { snprintf(nm, sizeof(nm), "%s.t32", base); save(nm, t32, (size_t)nn * 4); }
/* frame metadata + shutter: the frame buffer base at dev+0x50 */
unsigned char *fb = *(unsigned char **)(dev + 0x50);
if (!is_ro_ptr(fb, 0x4c + 38400 + 8)) fb = NULL;
unsigned int fcnt = *(unsigned int *)(dev + 8);
unsigned int shutter = fb ? *(unsigned int *)(fb + 0x2c) : 0;
unsigned int shutter2 = fb ? *(unsigned int *)(fb + 0x28) : 0;
unsigned int ftype = fb ? *(unsigned int *)(fb + 0x40) : 0;
unsigned int dev54 = *(unsigned int *)(dev + 0x54);
unsigned int dev5c = *(unsigned int *)(dev + 0x5c);
unsigned int dev60 = *(unsigned int *)(dev + 0x60);
unsigned int win_hi = *(unsigned int *)(dev + 0x4202c);
unsigned int win_lo = *(unsigned int *)(dev + 0x42030);
unsigned int s_max = *(unsigned int *)(dev + 0x42010);
unsigned int s_min = *(unsigned int *)(dev + 0x42014);
unsigned int s_mean = *(unsigned int *)(dev + 0x42018);
unsigned int s_std = *(unsigned int *)(dev + 0x4201c);
unsigned int sel = *(unsigned int *)(dev + 0x41840);
snprintf(nm, sizeof(nm), "%s.meta", base);
{
unsigned int v[16] = {fcnt, shutter, shutter2, ftype, dev54, dev5c, dev60,
win_hi, win_lo, s_max, s_min, s_mean, s_std, sel,
*(unsigned int *)(dev + 0x41558), *(unsigned int *)(dev + 0x41554)};
FILE *f = fopen(nm, "wb");
if (f) { fwrite(v, 4, 16, f); fclose(f); }
}
/* window + stats + lut + hist */
snprintf(nm, sizeof(nm), "%s.win", base);
{
FILE *f = fopen(nm, "wb");
if (f) {
unsigned int v[8] = {win_hi, win_lo, s_max, s_min, s_mean, s_std,
*(unsigned int *)(dev + 0x42020), *(unsigned int *)(dev + 0x42024)};
fwrite(v, 4, 8, f); fclose(f);
}
}
snprintf(nm, sizeof(nm), "%s.lut", base); save(nm, dev + 0x4284c, 1024);
snprintf(nm, sizeof(nm), "%s.hist", base); save(nm, dev + 0x4204c, 256 * 4);
snprintf(nm, sizeof(nm), "%s.h1024", base); save(nm, dev + 0x42c4c, 1024 * 4);
snprintf(nm, sizeof(nm), "%s.cdf", base); save(nm, dev + 0x43c4c, 1024 * 4);
/* ref + f20 */
unsigned short *refp = *(unsigned short **)(dev + 0x41ef0);
if (is_ro_ptr(refp, (size_t)nn * 2)) { snprintf(nm, sizeof(nm), "%s.ref", base); save(nm, refp, (size_t)nn * 2); }
void *f20 = *(void **)(dev + 0x41f20);
if (is_ro_ptr(f20, (size_t)nn * 2)) { snprintf(nm, sizeof(nm), "%s.f20", base); save(nm, f20, (size_t)nn * 2); }
/* full working tables + endpoints (sel/sel+1) */
{
int nsegs = *(int *)(dev + 0x41554);
int c5c = *(int *)(dev + 0x4155c);
size_t thr_sz = (size_t)(nsegs - 1) * nn * 2 + (size_t)c5c;
size_t g_sz = (size_t)nsegs * nn * 4;
void *p;
p = *(void **)(dev + 0x41848); if (is_ro_ptr(p, thr_sz)) { snprintf(nm, sizeof(nm), "%s.thr0", base); save(nm, p, thr_sz); }
p = *(void **)(dev + 0x41850); if (is_ro_ptr(p, thr_sz)) { snprintf(nm, sizeof(nm), "%s.thr1", base); save(nm, p, thr_sz); }
p = *(void **)(dev + 0x41858); if (is_ro_ptr(p, (size_t)(nsegs - 1) * nn * 2)) { snprintf(nm, sizeof(nm), "%s.thrw", base); save(nm, p, (size_t)(nsegs - 1) * nn * 2); }
p = *(void **)(dev + 0x41860); if (is_ro_ptr(p, g_sz)) { snprintf(nm, sizeof(nm), "%s.g0", base); save(nm, p, g_sz); }
p = *(void **)(dev + 0x41868); if (is_ro_ptr(p, g_sz)) { snprintf(nm, sizeof(nm), "%s.g1", base); save(nm, p, g_sz); }
p = *(void **)(dev + 0x41870); if (is_ro_ptr(p, g_sz)) { snprintf(nm, sizeof(nm), "%s.gw", base); save(nm, p, g_sz); }
p = *(void **)(dev + 0x41878); if (is_ro_ptr(p, 0x1000)) {
int sel2 = *(int *)(dev + 0x41840);
int bc = sel2 >= 0 && sel2 < 64 ? *(int *)(dev + 0x41654 + (size_t)sel2 * 4) : 0;
if (bc > 0 && bc < 100000 && is_ro_ptr(p, (size_t)bc * 40)) {
snprintf(nm, sizeof(nm), "%s.blind", base);
save(nm, p, (size_t)bc * 40);
printf(" blind records: %d\n", bc);
}
}
}
/* frame header bytes + tail (raw USB frame, from the parser buffer) */
{
unsigned char *rb = *(unsigned char **)(dev + 0x2d20);
if (is_ro_ptr(rb, 0x1c + 0x9600 + 0x100)) {
snprintf(nm, sizeof(nm), "%s.fhdr", base);
save(nm, rb, 0x40);
snprintf(nm, sizeof(nm), "%s.ftail", base);
save(nm, rb + 0x1c + 0x9600, 0x100);
}
}
printf("[%d] fcnt=%u shutter=%u/%u type=%u dev54=%u dev5c=%u dev60=%u sel=%u win=[%u,%u] stats=(%u..%u m%u s%u)\n",
k, fcnt, shutter, shutter2, ftype, dev54, dev5c, dev60, sel,
win_hi, win_lo, s_max, s_min, s_mean, s_std);
fflush(stdout);
Sleep(100);
}
T_Stop();
printf("done\n");
return 0;
}