code wiki / _hdl_build / nx_nxfh_lib.nx
nx_nxfh_lib.nx source
↩ module page · 169 lines · 6098 B
1// nx_nxfh_lib.nx -- shared NXFH1 text-hex framebuffer codec (LIB, no main). license_tier: ORIGINAL
2// WHY A LIB (DRY, rule 15): the NXFH1 parser lived ONLY inside nx_frame_score.nx (an organ with a
3// main, so nothing could import it); the moment a second consumer existed (nx_frame_score2 + gates)
4// copies #2 and #3 were about to happen. This is the extraction. The DEPLOYED nx_frame_score keeps
5// its private copy untouched -- this task builds a new instrument, it does not touch the live one.
6// FORMAT NXFH1 (deliberately TEXT so it travels the fs-write lane safely; raw binary through
7// JSON-string tooling is the proven corruption path):
8// line 1: "NXFH1 <w> <h>\n" then w*h pixels as 6 lowercase hex chars each (rrggbb), any
9// whitespace between pixels ignored. Decoded pixel layout r | g<<8 | b<<16 = the critic fb layout.
10// CONTRACT: nxl_load REFUSES (returns 0) on missing file, bad magic, junk bytes, short/overlong
11// pixel stream -- a truncated capture must never be scored as a dark frame. nxl_dump is the writer;
12// a writer is only tested by a read-back (banked law), which nx_craft_capture_gate enforces.
13import "nx_syscalls.nx"
14
15const NXL_MAXW: i64 = 1024
16const NXL_MAXH: i64 = 1024
17const NXL_MAGIC_65536: i64 = 65536
18const NXL_MODE_644: i64 = 420
19const NXL_HDRMIN: i64 = 12
20
21func nxl_isws(c: i64) -> i64 { if c==32 { return 1 } if c==10 { return 1 } if c==13 { return 1 } if c==9 { return 1 } return 0 }
22func nxl_nib(c: i64) -> i64 {
23 if c >= 48 { if c <= 57 { return c - 48 } }
24 if c >= 97 { if c <= 102 { return c - 87 } }
25 if c >= 65 { if c <= 70 { return c - 55 } }
26 return 0-1
27}
28
29// Load an NXFH1 file into a freshly mmapped packed fb (r | g<<8 | b<<16). On success writes w,h to
30// whp[0],whp[1] and returns the fb ptr; on ANY malformation returns 0 (refuse loudly, never guess).
31func nxl_load(path: *u8, whp: *i64) -> *i64 {
32 let lenp: *i64 = sys_mmap(8) as *i64
33 lenp[0] = 0
34 let b: *u8 = sys_read_file(path, lenp)
35 if (b as i64)==0 { return 0 as *i64 }
36 let n: i64 = lenp[0]
37 if n < NXL_HDRMIN { return 0 as *i64 }
38 if b[0]!=(78 as u8) { return 0 as *i64 }
39 if b[1]!=(88 as u8) { return 0 as *i64 }
40 if b[2]!=(70 as u8) { return 0 as *i64 }
41 if b[3]!=(72 as u8) { return 0 as *i64 }
42 if b[4]!=(49 as u8) { return 0 as *i64 }
43 var p: i64 = 5
44 var run: i64 = 1
45 while run==1 {
46 if p >= n { run = 0 } else {
47 if nxl_isws(b[p] as i64)==1 { p = p + 1 } else { run = 0 }
48 }
49 }
50 var w: i64 = 0
51 run = 1
52 while run==1 {
53 if p >= n { run = 0 } else {
54 let c: i64 = b[p] as i64
55 if c < 48 { run = 0 } else {
56 if c > 57 { run = 0 } else { w = w*10 + (c-48); p = p + 1 }
57 }
58 }
59 }
60 run = 1
61 while run==1 {
62 if p >= n { run = 0 } else {
63 if nxl_isws(b[p] as i64)==1 { p = p + 1 } else { run = 0 }
64 }
65 }
66 var h: i64 = 0
67 run = 1
68 while run==1 {
69 if p >= n { run = 0 } else {
70 let c2: i64 = b[p] as i64
71 if c2 < 48 { run = 0 } else {
72 if c2 > 57 { run = 0 } else { h = h*10 + (c2-48); p = p + 1 }
73 }
74 }
75 }
76 if w < 8 { return 0 as *i64 }
77 if h < 8 { return 0 as *i64 }
78 if w > NXL_MAXW { return 0 as *i64 }
79 if h > NXL_MAXH { return 0 as *i64 }
80 let need: i64 = w*h
81 let fb: *i64 = sys_mmap(need*8) as *i64
82 var px: i64 = 0
83 var acc: i64 = 0
84 var nyb: i64 = 0
85 var bad: i64 = 0
86 while p < n {
87 let c3: i64 = b[p] as i64
88 if nxl_isws(c3)==0 {
89 let v: i64 = nxl_nib(c3)
90 if v < 0 { bad = 1; p = n } else {
91 acc = acc*16 + v
92 nyb = nyb + 1
93 if nyb == 6 {
94 if px >= need { bad = 1; p = n } else {
95 let r: i64 = (acc / NXL_MAGIC_65536) % 256
96 let g: i64 = (acc / 256) % 256
97 let bb: i64 = acc % 256
98 fb[px] = r + g*256 + bb*NXL_MAGIC_65536
99 px = px + 1
100 acc = 0
101 nyb = 0
102 }
103 }
104 }
105 }
106 if p < n { p = p + 1 }
107 }
108 if bad == 1 { return 0 as *i64 }
109 if px != need { return 0 as *i64 }
110 if nyb != 0 { return 0 as *i64 }
111 whp[0] = w
112 whp[1] = h
113 return fb
114}
115
116func nxl_cat(d: *u8, at: i64, s: *u8) -> i64 { var i: i64=0; var a: i64=at; while s[i]!=(0 as u8) { d[a]=s[i]; a=a+1; i=i+1 } return a }
117func nxl_catn(d: *u8, at: i64, v: i64) -> i64 {
118 var a: i64 = at
119 var m: i64 = v
120 if m == 0 { d[a] = 48 as u8; return a + 1 }
121 if m < 0 { d[a] = 45 as u8; a = a + 1; m = 0 - m }
122 let t: *u8 = sys_mmap(32)
123 var k: i64 = 0
124 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
125 var q: i64 = k - 1
126 while q >= 0 { d[a] = t[q]; a = a + 1; q = q - 1 }
127 return a
128}
129func nxl_hex2(d: *u8, at: i64, v: i64) -> i64 {
130 let hi: i64 = (v / 16) % 16
131 let lo: i64 = v % 16
132 var c1: i64 = 48 + hi
133 if hi > 9 { c1 = 87 + hi }
134 var c2: i64 = 48 + lo
135 if lo > 9 { c2 = 87 + lo }
136 d[at] = c1 as u8
137 d[at+1] = c2 as u8
138 return at + 2
139}
140
141// Write fb as NXFH1 text. Returns bytes written (>0) or -1 on open failure.
142func nxl_dump(path: *u8, fb: *i64, w: i64, h: i64) -> i64 {
143 let cap: i64 = 64 + w*h*6 + h
144 let d: *u8 = sys_mmap(cap)
145 var o: i64 = nxl_cat(d, 0, "NXFH1 " as *u8)
146 o = nxl_catn(d, o, w)
147 d[o] = 32 as u8; o = o + 1
148 o = nxl_catn(d, o, h)
149 d[o] = 10 as u8; o = o + 1
150 var y: i64 = 0
151 while y < h {
152 var x: i64 = 0
153 while x < w {
154 let v: i64 = fb[y*w+x]
155 o = nxl_hex2(d, o, v % 256)
156 o = nxl_hex2(d, o, (v / 256) % 256)
157 o = nxl_hex2(d, o, (v / NXL_MAGIC_65536) % 256)
158 x = x + 1
159 }
160 d[o] = 10 as u8
161 o = o + 1
162 y = y + 1
163 }
164 let fd: i64 = sys_openat_wr(path, NXL_MODE_644)
165 if fd < 0 { return 0-1 }
166 sys_write(fd, d, o)
167 sys_close(fd)
168 return o
169}