code wiki / (root) / nx_nxa_texc_lib.nx

nx_nxa_texc_lib.nx source

↩ module page · 404 lines · 18760 B

1// nx_nxa_texc_lib.nx -- ★TEXC: UV TEXTURE COORDINATES AS A FORMAT CAPABILITY (debt 1785794796 rung 1). 2// The measured fact that forced this: ref9.nxa declares VERT TRIS CLUS SKEL SKIN ANIM HSTR GVRT GTRI 3// GPIN POSE -- NO TEXC, NO UV -- so sampling ANY texture map was IMPOSSIBLE, and 'we shade procedurally 4// from genome' was a CONSTRAINT wearing the costume of a choice. This lib computes a bone-anchored 5// cylindrical unwrap from sections the asset ALREADY carries (SKEL world bind positions + SKIN top-4 6// weights) and writes a TEXC section: every vertex gets a stable atlas coordinate AND a region tag. 7// 8// UNWRAP (integer, deterministic): per vertex take its PRIMARY bone (max SKIN weight slot). v = clamped 9// position along the parent->joint bind segment (Q12 -> Q16). u = angle around the bone's DOMINANT axis 10// (rt_atan2, full circle 25736 it-units -> Q16). Each joint owns one TILE of a g x g atlas grid 11// (g = ceil(sqrt(nj))), so charts from different bones are DISJOINT BY CONSTRUCTION -- a bake and a 12// sampler sharing this section can never bleed one body part into another. Seams sit at weight 13// boundaries (rigid ownership); bake+sample use the SAME mapping so sampling stays self-consistent. 14// 15// TEXC payload (i64 words): [0]=nv [1]=stride(3) [2]=atlas grid g [3]=reserved(0), then per vertex: 16// [u q16 0..65535][v q16 0..65535][owning joint]. The joint word IS the region tag (rung 2 seed): 17// finer than Face|Torso|Limbs and groupable through SKEL parents later. 18// 19// ADDITIVE AND IDEMPOTENT like its siblings (nx_nxa_dyna): the input is never modified; an existing 20// TEXC is REPLACED, so running twice is identical to running once. FAIL-CLOSED: refuses with a NAMED 21// reason rather than fabricate (no SKIN -> no invented ownership; count mismatch -> refuse). 22// license_tier: ORIGINAL No hw writes (Rule 26). 23import "nx_syscalls.nx" 24import "nx_nxa.nx" 25import "nx_pose_retarget.nx" 26 27const NT_HDR: i64 = 32 28const NT_TOCE: i64 = 32 29const NT_MAXSEC: i64 = 64 30const NT_MODE: i64 = 0x1a4 31const NT_ERRFD: i64 = 2 32const NT_Q12: i64 = 4096 33const NT_Q16: i64 = 65536 34const NT_Q16M: i64 = 65535 35const NT_HALFC: i64 = 12868 36const NT_FULLC: i64 = 25736 37const NT_PRESH: i64 = 16 38const NT_STRIDE: i64 = 3 39const NT_PHDR: i64 = 4 40const NT_PAD: i64 = 4096 41 42// ---- GE67b: AREA PER REGION, NOT AREA PER JOINT --------------------------------------------- 43// MEASURED DEFECT (nx_craft_emit cw_chart_adequacy, first live reading 2026-09-07): a uniform 44// g x g grid gave all 104 joints an EQUAL 186-texel tile while their bind spans differ by more 45// than tenfold -- head 95510um at 513um/texel carrying 1 of 4 declared skin bands, longest bone 46// 1010390um at 5432um/texel carrying 0 of 4. One tile size cannot serve both. 47// THE SOURCE FOR THE SPLIT IS A CORPUS, NOT A PREFERENCE: knowledge/asset_texture_floor.conf, 48// measured 2026-08-03 from 23 real shipped packages, records tex_region_count=4, tex_map_set=4 49// and tex_maps_total=16 -- four regions, an EQUAL map set each -- with tex_res_floor=4096 read 50// off a FACE map. Equal maps per region is equal AREA per region, so the face takes one quarter 51// of the atlas and the rest of the body shares the other three quarters. Nothing is tuned. 52// WHY NOT EQUAL TEXEL DENSITY, THE OBVIOUS ALTERNATIVE: density-equalisation makes tile side 53// proportional to span, which would SHRINK the head (a small region) to about half the texels it 54// has today. It is the right rule for a uniform surface and the wrong one for a face, and the 55// corpus is what says so. 56// SCOPE, DECLARED RATHER THAN GLOSSED: the face region here is the SINGLE highest-bound joint -- 57// the same head the adequacy check already names, so there is one convention and no new 58// threshold constant. A multi-joint head (jaw, eyes, brow) is not yet split out and is the next 59// step; torso, limbs and gens are not yet separated from each other and share the three 60// remaining quadrants uniformly, which is why this is a quarter-and-the-rest and not yet 4x4. 61const NT_RECT_W: i64 = 4 // words per joint in the rect table: ox, oy, w, h (Q16) 62const NT_REST_Q: i64 = 3 // the three quadrants the non-face joints share 63const NT_MIN_CELL: i64 = 3 // a cell narrower than this has no interior after the 1px inset 64// the atlas quadrant a joint owns: q0 is the face, q1..q3 tile the rest 65func ntx_quad_ox(q: i64) -> i64 { if q == 1 { return NT_Q16/2 } if q == 3 { return NT_Q16/2 } return 0 } 66func ntx_quad_oy(q: i64) -> i64 { if q == 2 { return NT_Q16/2 } if q == 3 { return NT_Q16/2 } return 0 } 67 68const NT_OK: i64 = 0 69const NT_E_IO: i64 = 1 70const NT_E_BAD: i64 = 3 71const NT_E_NOSEC: i64 = 4 72const NT_E_MISMATCH: i64 = 5 73 74func nt_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 75func nt_err(s: *u8) -> i64 { sys_write(NT_ERRFD, s, nt_slen(s)); return 0 } 76func nt_outs(s: *u8) -> i64 { sys_write(1, s, nt_slen(s)); return 0 } 77func nt_outn(v: i64) -> i64 { 78 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 } 79 var m: i64 = v 80 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 81 let t: *u8 = sys_mmap(32) 82 var k: i64 = 0 83 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 84 let o: *u8 = sys_mmap(32) 85 var i: i64 = 0 86 while i < k { o[i] = t[k - 1 - i]; i = i + 1 } 87 sys_write(1, o, k) 88 return 0 89} 90func nt_rd64(b: *u8, off: i64) -> i64 { 91 var v: i64 = 0 92 var i: i64 = 7 93 while i >= 0 { v = v*256 + ((b[off + i] & 0xff) as i64); i = i - 1 } 94 return v 95} 96func nt_wr64(b: *u8, off: i64, v: i64) -> i64 { 97 var m: i64 = v 98 var i: i64 = 0 99 while i < 8 { b[off + i] = (m & 0xff) as u8; m = m >> 8; i = i + 1 } 100 return 0 101} 102func nt_tageq(b: *u8, off: i64, t: *u8) -> i64 { 103 var i: i64 = 0 104 while i < 4 { if b[off + i] != t[i] { return 0 } i = i + 1 } 105 return 1 106} 107func nt_grid(n: i64) -> i64 { 108 var g: i64 = 1 109 while g*g < n { g = g + 1 } 110 return g 111} 112 113// ---- THE INVERSE OF THE ALLOCATION, OWNED BY THE ORGAN THAT DECIDES IT -------------------- 114// GE67b changed the atlas layout and IMMEDIATELY broke its sibling: nx_nxa_texbake_lib maps a 115// texel to its joint at THREE sites with ty*g+tx, the uniform-grid arithmetic, which after a 116// per-region allocation names the wrong joint -- so the face quadrant took torso and limb tones 117// and texels past nj fell to the VOID grey. A FIX THAT LIVES IN ONE ORGAN AND NOT ITS SIBLING IS 118// HALF A FIX, and the missing half is invisible until something runs it. 119// The remedy is not a second copy of the layout in the baker: the organ that DECIDES the rects 120// exports the inverse, so the two cannot disagree. Rasterises each joint's rect into a res x res 121// joint-id map in ONE pass over the atlas (total work = sum of rect areas = res^2), and falls 122// back to the legacy uniform grid when word 3 is 0, which is exactly what an old asset carries. 123// An unowned texel reads nj -- outside every caller's existing [0,nj) range test, so it still 124// paints the VOID grey and a sampling leak stays VISIBLE, with no call site needing a new branch. 125func ntx_jmap(bb: *u8, flen: i64, res: i64, nj: i64, jm: *i64) -> i64 { 126 let xwo: i64 = nxa_find(bb, flen, nxa_tag4("TEXC" as *u8)) 127 if xwo < 0 { return 0 - 1 } 128 let w: *i64 = bb as *i64 129 let g: i64 = w[xwo + 2] 130 let ro: i64 = w[xwo + 3] 131 if g < 1 { return 0 - 1 } 132 var i: i64 = 0 133 while i < res*res { jm[i] = nj; i = i + 1 } 134 // LEGACY: no rect table, so the uniform cell IS the layout and reproducing it here is not a 135 // duplicate convention -- it is the same arithmetic the same organ wrote. 136 if ro <= 0 { 137 var y: i64 = 0 138 while y < res { 139 let ty: i64 = y*g/res 140 var x: i64 = 0 141 while x < res { 142 jm[y*res + x] = ty*g + x*g/res 143 x = x + 1 144 } 145 y = y + 1 146 } 147 return 0 148 } 149 var j: i64 = 0 150 while j < nj { 151 let rb: i64 = xwo + ro + j*NT_RECT_W 152 let x0: i64 = w[rb]*res/NT_Q16 153 let y0: i64 = w[rb + 1]*res/NT_Q16 154 let x1: i64 = (w[rb] + w[rb + 2])*res/NT_Q16 155 let y1: i64 = (w[rb + 1] + w[rb + 3])*res/NT_Q16 156 var yy: i64 = y0 157 while yy < y1 { 158 if yy >= 0 { if yy < res { 159 var xx: i64 = x0 160 while xx < x1 { 161 if xx >= 0 { if xx < res { jm[yy*res + xx] = j } } 162 xx = xx + 1 163 } 164 } } 165 yy = yy + 1 166 } 167 j = j + 1 168 } 169 return 0 170} 171 172// core: read in.nxa, compute the unwrap, write out.nxa with a TEXC section. NT_OK or named refusal. 173func ntx_apply(inp: *u8, outp: *u8) -> i64 { 174 let lp: *i64 = sys_mmap(16) as *i64 175 let b: *u8 = sys_read_file(inp, lp) 176 if (b as i64) == 0 { nt_err("TEXC-REFUSE cannot read input NXA\n" as *u8); return NT_E_IO } 177 let flen: i64 = lp[0] 178 if flen < NT_HDR { nt_err("TEXC-REFUSE file too short to be an NXA\n" as *u8); return NT_E_BAD } 179 if nt_rd64(b, 0) != nxa_magic() { nt_err("TEXC-REFUSE not an NXA (bad magic)\n" as *u8); return NT_E_BAD } 180 if nt_rd64(b, 8) > NXA_VER { nt_err("TEXC-REFUSE future NXA version\n" as *u8); return NT_E_BAD } 181 let ns: i64 = nt_rd64(b, 16) 182 if ns < 1 { nt_err("TEXC-REFUSE section count invalid\n" as *u8); return NT_E_BAD } 183 if ns >= NT_MAXSEC { nt_err("TEXC-REFUSE section table full\n" as *u8); return NT_E_BAD } 184 if flen < NT_HDR + ns*NT_TOCE { nt_err("TEXC-REFUSE toc runs past EOF\n" as *u8); return NT_E_BAD } 185 var vo: i64 = 0 - 1 186 var ko: i64 = 0 - 1 187 var so: i64 = 0 - 1 188 var txi: i64 = 0 - 1 189 var s: i64 = 0 190 while s < ns { 191 let e: i64 = NT_HDR + s*NT_TOCE 192 if nt_tageq(b, e, "VERT" as *u8) == 1 { vo = nt_rd64(b, e + 8) } 193 if nt_tageq(b, e, "SKIN" as *u8) == 1 { ko = nt_rd64(b, e + 8) } 194 if nt_tageq(b, e, "SKEL" as *u8) == 1 { so = nt_rd64(b, e + 8) } 195 if nt_tageq(b, e, "TEXC" as *u8) == 1 { txi = s } 196 s = s + 1 197 } 198 if vo < 0 { nt_err("TEXC-REFUSE no VERT section\n" as *u8); return NT_E_NOSEC } 199 if so < 0 { nt_err("TEXC-REFUSE no SKEL section -- nothing anchors the unwrap\n" as *u8); return NT_E_NOSEC } 200 if ko < 0 { nt_err("TEXC-REFUSE no SKIN section -- refusing to fabricate bone ownership\n" as *u8); return NT_E_NOSEC } 201 let nv: i64 = nt_rd64(b, vo) 202 let nj: i64 = nt_rd64(b, so) 203 let nk: i64 = nt_rd64(b, ko) 204 if nv < 1 { nt_err("TEXC-REFUSE empty VERT\n" as *u8); return NT_E_BAD } 205 if nj < 1 { nt_err("TEXC-REFUSE empty SKEL\n" as *u8); return NT_E_BAD } 206 if nk != nv { nt_err("TEXC-REFUSE SKIN count does not match VERT count\n" as *u8); return NT_E_MISMATCH } 207 if vo + (1 + nv*3)*8 > flen { nt_err("TEXC-REFUSE VERT runs past EOF\n" as *u8); return NT_E_BAD } 208 if so + (1 + nj*8)*8 > flen { nt_err("TEXC-REFUSE SKEL runs past EOF\n" as *u8); return NT_E_BAD } 209 if ko + (1 + nv*8)*8 > flen { nt_err("TEXC-REFUSE SKIN runs past EOF\n" as *u8); return NT_E_BAD } 210 let g: i64 = nt_grid(nj) 211 let tw: i64 = NT_Q16 / g 212 // GE67b: the rect table rides the payload TAIL, so a reader that walks nv rows and stops is 213 // byte-for-byte unaffected; word 3 was reserved(0) and now carries the table's word offset, 214 // which is exactly the 0-means-legacy-uniform-grid signal an old asset already gives. 215 let rectoff: i64 = NT_PHDR + nv*NT_STRIDE 216 let uvw: *i64 = sys_mmap((rectoff + nj*NT_RECT_W)*8 + 64) as *i64 217 uvw[0] = nv 218 uvw[1] = NT_STRIDE 219 uvw[2] = g 220 uvw[3] = rectoff 221 // the face region is the highest-bound joint: the same head nx_craft_emit's adequacy check 222 // names, so the two organs cannot disagree about which chart the close-up reads. 223 var fj: i64 = 0 224 var fz: i64 = nt_rd64(b, so + 8 + 3*8) 225 var j0: i64 = 1 226 while j0 < nj { 227 let z0: i64 = nt_rd64(b, so + 8 + (j0*8 + 3)*8) 228 if z0 > fz { fz = z0; fj = j0 } 229 j0 = j0 + 1 230 } 231 let nrest: i64 = nj - 1 232 var per: i64 = (nrest + NT_REST_Q - 1) / NT_REST_Q 233 if per < 1 { per = 1 } 234 let gr: i64 = nt_grid(per) 235 let half: i64 = NT_Q16 / 2 236 let cell: i64 = half / gr 237 // A cell with no interior after the 1-unit inset would collapse every chart in it to a point. 238 // Refuse rather than emit an unwrap whose charts are empty -- fail closed, named. 239 if cell < NT_MIN_CELL { nt_err("TEXC-REFUSE too many joints for the atlas: a rest cell has no interior\n" as *u8); return NT_E_BAD } 240 var jr: i64 = 0 241 var ri: i64 = 0 242 while jr < nj { 243 let rb: i64 = rectoff + jr*NT_RECT_W 244 if jr == fj { 245 uvw[rb] = 0 246 uvw[rb + 1] = 0 247 uvw[rb + 2] = half 248 uvw[rb + 3] = half 249 } 250 if jr != fj { 251 let q: i64 = ri % NT_REST_Q + 1 252 let c: i64 = ri / NT_REST_Q 253 uvw[rb] = ntx_quad_ox(q) + (c % gr)*cell 254 uvw[rb + 1] = ntx_quad_oy(q) + (c / gr)*cell 255 uvw[rb + 2] = cell 256 uvw[rb + 3] = cell 257 ri = ri + 1 258 } 259 jr = jr + 1 260 } 261 let jused: *i64 = sys_mmap(nj*8 + 64) as *i64 262 var i: i64 = 0 263 while i < nv { 264 var pj: i64 = nt_rd64(b, ko + 8 + (i*8)*8) 265 var pw: i64 = nt_rd64(b, ko + 8 + (i*8 + 4)*8) 266 var sl: i64 = 1 267 while sl < 4 { 268 let jj: i64 = nt_rd64(b, ko + 8 + (i*8 + sl)*8) 269 let ww: i64 = nt_rd64(b, ko + 8 + (i*8 + 4 + sl)*8) 270 if ww > pw { pw = ww; pj = jj } 271 sl = sl + 1 272 } 273 if pj < 0 { nt_err("TEXC-REFUSE SKIN joint index negative\n" as *u8); return NT_E_MISMATCH } 274 if pj >= nj { nt_err("TEXC-REFUSE SKIN joint index out of SKEL range\n" as *u8); return NT_E_MISMATCH } 275 let par: i64 = nt_rd64(b, so + 8 + (pj*8)*8) 276 let jx: i64 = nt_rd64(b, so + 8 + (pj*8 + 1)*8) 277 let jy: i64 = nt_rd64(b, so + 8 + (pj*8 + 2)*8) 278 let jz: i64 = nt_rd64(b, so + 8 + (pj*8 + 3)*8) 279 var ox: i64 = jx 280 var oy: i64 = jy 281 var oz: i64 = jz 282 var ax: i64 = 0 283 var ay: i64 = 0 284 var az: i64 = 1000 285 if par >= 0 { if par < nj { 286 ox = nt_rd64(b, so + 8 + (par*8 + 1)*8) 287 oy = nt_rd64(b, so + 8 + (par*8 + 2)*8) 288 oz = nt_rd64(b, so + 8 + (par*8 + 3)*8) 289 ax = jx - ox 290 ay = jy - oy 291 az = jz - oz 292 } } 293 if ax == 0 { if ay == 0 { if az == 0 { az = 1000 } } } 294 let vx: i64 = nt_rd64(b, vo + 8 + (i*3)*8) 295 let vy: i64 = nt_rd64(b, vo + 8 + (i*3 + 1)*8) 296 let vz: i64 = nt_rd64(b, vo + 8 + (i*3 + 2)*8) 297 let dxp: i64 = (vx - ox) / NT_PRESH 298 let dyp: i64 = (vy - oy) / NT_PRESH 299 let dzp: i64 = (vz - oz) / NT_PRESH 300 let axp: i64 = ax / NT_PRESH 301 let ayp: i64 = ay / NT_PRESH 302 let azp: i64 = az / NT_PRESH 303 let den: i64 = axp*axp + ayp*ayp + azp*azp 304 var tq: i64 = 0 305 if den > 0 { tq = (dxp*axp + dyp*ayp + dzp*azp)*NT_Q12/den } 306 if tq < 0 { tq = 0 } 307 if tq > NT_Q12 { tq = NT_Q12 } 308 var aax: i64 = ax 309 if aax < 0 { aax = 0 - aax } 310 var aay: i64 = ay 311 if aay < 0 { aay = 0 - aay } 312 var aaz: i64 = az 313 if aaz < 0 { aaz = 0 - aaz } 314 var p1: i64 = dxp 315 var p2: i64 = dyp 316 if aax >= aay { if aax >= aaz { p1 = dyp; p2 = dzp } } 317 if aay > aax { if aay >= aaz { p1 = dzp; p2 = dxp } } 318 let ang: i64 = rt_atan2(p2, p1) 319 let u01: i64 = (ang + NT_HALFC) * NT_Q16M / NT_FULLC 320 let v01: i64 = tq * NT_Q16M / NT_Q12 321 // GE67b: the chart is the joint's ALLOCATED rectangle, not a cell of a uniform grid. 322 let rq: i64 = rectoff + pj*NT_RECT_W 323 let rx: i64 = uvw[rq] 324 let ry: i64 = uvw[rq + 1] 325 let rw: i64 = uvw[rq + 2] 326 let rh: i64 = uvw[rq + 3] 327 let uu: i64 = rx + 1 + u01*(rw - 2)/NT_Q16 328 let vv: i64 = ry + 1 + v01*(rh - 2)/NT_Q16 329 uvw[NT_PHDR + i*NT_STRIDE] = uu 330 uvw[NT_PHDR + i*NT_STRIDE + 1] = vv 331 uvw[NT_PHDR + i*NT_STRIDE + 2] = pj 332 jused[pj] = 1 333 i = i + 1 334 } 335 // ---- build the output: keep every section except an old TEXC, append ours, recompute checks ---- 336 var nsec: i64 = ns 337 if txi < 0 { nsec = ns + 1 } 338 let twords: i64 = rectoff + nj*NT_RECT_W 339 let toclen: i64 = NT_HDR + nsec*NT_TOCE 340 var total: i64 = toclen 341 var s2: i64 = 0 342 while s2 < ns { 343 if s2 != txi { 344 let e2: i64 = NT_HDR + s2*NT_TOCE 345 total = total + nt_rd64(b, e2 + 16)*8 346 } 347 s2 = s2 + 1 348 } 349 total = total + twords*8 350 let nb: *u8 = sys_mmap(total + NT_PAD) 351 if (nb as i64) == 0 { nt_err("TEXC-REFUSE cannot allocate output\n" as *u8); return NT_E_IO } 352 nt_wr64(nb, 0, nxa_magic()) 353 nt_wr64(nb, 8, NXA_VER) 354 nt_wr64(nb, 16, nsec) 355 var wo: i64 = toclen 356 var ti: i64 = 0 357 var s3: i64 = 0 358 while s3 < ns { 359 if s3 != txi { 360 let e3: i64 = NT_HDR + s3*NT_TOCE 361 let oldoff: i64 = nt_rd64(b, e3 + 8) 362 let wl: i64 = nt_rd64(b, e3 + 16) 363 let te: i64 = NT_HDR + ti*NT_TOCE 364 nt_wr64(nb, te, nt_rd64(b, e3)) 365 nt_wr64(nb, te + 8, wo) 366 nt_wr64(nb, te + 16, wl) 367 var k2: i64 = 0 368 while k2 < wl*8 { nb[wo + k2] = b[oldoff + k2]; k2 = k2 + 1 } 369 // recompute rather than copy the old checksum (a copied checksum validates its own bug) 370 let pw2: *i64 = ((nb as i64) + wo) as *i64 371 nt_wr64(nb, te + 24, nxa_check2(1, pw2, wl)) 372 wo = wo + wl*8 373 ti = ti + 1 374 } 375 s3 = s3 + 1 376 } 377 let te4: i64 = NT_HDR + ti*NT_TOCE 378 nt_wr64(nb, te4, nxa_tag4("TEXC" as *u8)) 379 nt_wr64(nb, te4 + 8, wo) 380 nt_wr64(nb, te4 + 16, twords) 381 var k3: i64 = 0 382 while k3 < twords { nt_wr64(nb, wo + k3*8, uvw[k3]); k3 = k3 + 1 } 383 let pw3: *i64 = ((nb as i64) + wo) as *i64 384 nt_wr64(nb, te4 + 24, nxa_check2(1, pw3, twords)) 385 wo = wo + twords*8 386 // toc checksum over the FINAL toc 387 let tb2: *i64 = ((nb as i64) + NT_HDR) as *i64 388 nt_wr64(nb, 24, nxa_check2(1, tb2, nsec*4)) 389 let fd: i64 = sys_openat_wr(outp, NT_MODE) 390 if fd < 0 { nt_err("TEXC-REFUSE cannot open output\n" as *u8); return NT_E_IO } 391 sys_write(fd, nb, wo) 392 sys_close(fd) 393 var ju: i64 = 0 394 var j5: i64 = 0 395 while j5 < nj { if jused[j5] == 1 { ju = ju + 1 } j5 = j5 + 1 } 396 nt_outs("TEXC-OK nv=" as *u8); nt_outn(nv) 397 nt_outs(" nj=" as *u8); nt_outn(nj) 398 nt_outs(" grid=" as *u8); nt_outn(g) 399 nt_outs(" joints_used=" as *u8); nt_outn(ju) 400 nt_outs(" v0_uv=" as *u8); nt_outn(uvw[NT_PHDR]); nt_outs("," as *u8); nt_outn(uvw[NT_PHDR+1]) 401 nt_outs(" bytes=" as *u8); nt_outn(wo) 402 nt_outs("\n" as *u8) 403 return NT_OK 404}