1use alloc::string::String;
31use alloc::vec;
32use alloc::vec::Vec;
33
34use rt::fx::floor_i;
35
36use crate::ttf::{sfnt_name, Tb, NAME_CAP};
37
38pub const AXES_MAX: usize = 64;
40pub const INSTANCES_MAX: usize = 1024;
41pub(crate) const VAR_BUDGET: u32 = 1 << 22;
43const IVS_WORK: u32 = 1 << 16;
44
45const EMBEDDED_PEAK: u32 = 0x8000;
46const INTERMEDIATE: u32 = 0x4000;
47const PRIVATE_POINTS: u32 = 0x2000;
48const SHARED_POINTS: u32 = 0x8000;
49
50#[derive(Clone, Debug, PartialEq)]
52pub struct VarAxis {
53 pub tag: [u8; 4],
55 pub min: f32,
57 pub def: f32,
59 pub max: f32,
61 pub hidden: bool,
63 pub name: String,
65}
66
67#[derive(Clone, Debug, PartialEq)]
69pub struct VarInstance {
70 pub name: String,
72 pub coords: Vec<f32>,
74}
75
76pub const fn tag_u32(t: [u8; 4]) -> u32 {
78 u32::from_be_bytes(t)
79}
80
81struct Gvar {
83 shared: u32,
84 nshared: u32,
85 nglyphs: u32,
86 long: bool,
87 data: u32,
88}
89
90pub(crate) struct Var {
92 pub axes: Vec<VarAxis>,
93 pub instances: Vec<VarInstance>,
94 avar: Vec<Vec<(i32, i32)>>,
96 gvar: Option<Gvar>,
97 hvar: (u32, u32),
99 mvar: Vec<(u32, u16, u16)>,
101 mvar_store: u32,
102 gdef_store: u32,
104}
105
106fn r16(t: Tb, o: u32) -> Option<u32> {
109 let v = t.u16(o);
110 if v < 0 { None } else { Some(v as u32) }
111}
112
113fn s16(t: Tb, o: u32) -> Option<i32> {
114 r16(t, o).map(|v| v as u16 as i16 as i32)
115}
116
117fn r32(t: Tb, o: u32) -> Option<u32> {
118 let v = t.u32(o);
119 if v < 0 { None } else { Some(v as u32) }
120}
121
122fn fixed(v: u32) -> f32 {
123 v as i32 as f32 * (1.0 / 65536.0)
124}
125
126fn name_of(name: Tb, id: u32) -> String {
127 String::from_utf8_lossy(&sfnt_name(name, id, NAME_CAP)).into_owned()
128}
129
130fn at(a: u32, b: u32, c: u32) -> Option<u32> {
132 a.checked_add(b.checked_mul(c)?)
133}
134
135impl Var {
136 pub(crate) fn parse(fvar: Tb, avar: Tb, gvar: Tb, hvar: Tb, mvar: Tb, gdef: Tb, name: Tb) -> Option<Var> {
139 let t = fvar;
142 if r16(t, 0)? != 1 {
143 return None;
144 }
145 let ao = r16(t, 4)?;
146 let n = r16(t, 8)?;
147 let asz = r16(t, 10)?;
148 let ni = r16(t, 12)?;
149 let isz = r16(t, 14)?;
150 if n == 0 || n as usize > AXES_MAX || asz < 20 {
151 return None;
152 }
153 let mut axes = Vec::with_capacity(n as usize);
154 for i in 0..n {
155 let r = at(ao, i, asz)?;
156 let tag = r32(t, r)?;
157 let (mn, df, mx) = (fixed(r32(t, r + 4)?), fixed(r32(t, r + 8)?), fixed(r32(t, r + 12)?));
158 let flags = r16(t, r + 16)?;
159 let mut nm = name_of(name, r16(t, r + 18)?);
160 if nm.is_empty() {
161 nm = String::from_utf8_lossy(&tag.to_be_bytes()).into_owned();
162 }
163 axes.push(VarAxis { tag: tag.to_be_bytes(), min: mn.min(df), def: df, max: mx.max(df), hidden: flags & 1 != 0, name: nm });
165 }
166 let mut instances = Vec::new();
168 if isz >= 4 + 4 * n {
169 let io = at(ao, n, asz)?;
170 for j in 0..ni.min(INSTANCES_MAX as u32) {
171 let Some(r) = at(io, j, isz) else { break };
172 let mut coords = Vec::with_capacity(n as usize);
173 for k in 0..n {
174 let Some(v) = r32(t, r + 4 + 4 * k) else { break };
175 coords.push(fixed(v));
176 }
177 if coords.len() != n as usize {
178 break;
179 }
180 let Some(id) = r16(t, r) else { break };
181 instances.push(VarInstance { name: name_of(name, id), coords });
182 }
183 }
184 let mut v = Var {
185 axes,
186 instances,
187 avar: Vec::new(),
188 gvar: None,
189 hvar: (0, 0),
190 mvar: Vec::new(),
191 mvar_store: 0,
192 gdef_store: 0,
193 };
194 v.avar = Self::parse_avar(avar, n);
195 v.gvar = Self::parse_gvar(gvar, n);
196 if r16(hvar, 0) == Some(1) {
198 let store = r32(hvar, 4).unwrap_or(0);
199 if store != 0 && store < hvar.len() {
200 v.hvar = (store, r32(hvar, 8).unwrap_or(0));
201 }
202 }
203 if r16(mvar, 0) == Some(1) {
205 let rsz = r16(mvar, 6).unwrap_or(0);
206 let cnt = r16(mvar, 8).unwrap_or(0);
207 let store = r16(mvar, 10).unwrap_or(0);
208 if rsz >= 8 && store != 0 {
209 for i in 0..cnt {
210 let r = 12 + i * rsz;
211 let (Some(tag), Some(o), Some(inn)) = (r32(mvar, r), r16(mvar, r + 4), r16(mvar, r + 6)) else { break };
212 v.mvar.push((tag, o as u16, inn as u16));
213 }
214 v.mvar_store = store;
215 }
216 }
217 if r16(gdef, 0) == Some(1) && r16(gdef, 2).is_some_and(|m| m >= 3) {
219 v.gdef_store = r32(gdef, 14).unwrap_or(0);
220 }
221 Some(v)
222 }
223
224 fn parse_avar(t: Tb, n: u32) -> Vec<Vec<(i32, i32)>> {
227 let mut maps = Vec::new();
228 let (Some(major), Some(cnt)) = (r16(t, 0), r16(t, 6)) else { return maps };
229 if major != 1 && major != 2 {
230 return maps;
231 }
232 let mut o = 8u32;
233 for _ in 0..cnt.min(n) {
234 let Some(m) = r16(t, o) else { return Vec::new() };
235 let mut seg = Vec::with_capacity(m as usize);
236 for k in 0..m {
237 let p = o + 2 + k * 4;
238 let (Some(f), Some(to)) = (s16(t, p), s16(t, p + 2)) else { return Vec::new() };
239 seg.push((f, to));
240 }
241 maps.push(seg);
242 o += 2 + m * 4;
243 }
244 maps
245 }
246
247 fn parse_gvar(t: Tb, n: u32) -> Option<Gvar> {
250 if r16(t, 0)? != 1 || r16(t, 4)? != n {
251 return None;
252 }
253 let nshared = r16(t, 6)?;
254 let shared = r32(t, 8)?;
255 let nglyphs = r16(t, 12)?;
256 let long = r16(t, 14)? & 1 != 0;
257 let data = r32(t, 16)?;
258 let osz = if long { 4u64 } else { 2 };
259 if 20 + (nglyphs as u64 + 1) * osz > t.len() as u64 || shared as u64 + nshared as u64 * n as u64 * 2 > t.len() as u64 {
260 return None;
261 }
262 Some(Gvar { shared, nshared, nglyphs, long, data })
263 }
264
265 pub(crate) fn has_gvar(&self) -> bool {
266 self.gvar.is_some()
267 }
268
269 pub(crate) fn has_hvar(&self) -> bool {
270 self.hvar.0 != 0
271 }
272
273 pub(crate) fn normalize(&self, user: &[f32]) -> Vec<i16> {
278 let mut c = Vec::with_capacity(self.axes.len());
279 for (i, a) in self.axes.iter().enumerate() {
280 let mut v = user.get(i).copied().unwrap_or(a.def);
281 if v.is_nan() {
282 v = a.def;
283 }
284 v = v.clamp(a.min, a.max);
285 let n = if v == a.def {
287 0
288 } else if v < a.def {
289 floor_i((v - a.def) / (a.def - a.min) * 65536.0 + 0.5)
290 } else {
291 floor_i((v - a.def) / (a.max - a.def) * 65536.0 + 0.5)
292 };
293 let m = match self.avar.get(i) {
294 Some(seg) => avar_map(seg, n),
295 None => n,
296 };
297 c.push(((m.clamp(-65536, 65536) + 2) >> 2) as i16);
298 }
299 c
300 }
301
302 #[allow(clippy::too_many_arguments)]
309 pub(crate) fn gvar_deltas(&self, t: Tb, glyph: u32, coords: &[i16], orig: &[(f32, f32)], ends: &[usize], out: &mut [(f32, f32)], budget: &mut u32) -> bool {
310 let Some(gv) = &self.gvar else { return true };
311 let npts = out.len();
312 if glyph >= gv.nglyphs || coords.iter().all(|&c| c == 0) {
313 return true;
314 }
315 let (a, b) = if gv.long {
317 (r32(t, 20 + glyph * 4), r32(t, 24 + glyph * 4))
318 } else {
319 (r16(t, 20 + glyph * 2).map(|v| v * 2), r16(t, 22 + glyph * 2).map(|v| v * 2))
320 };
321 let (Some(a), Some(b)) = (a, b) else { return false };
322 if b <= a {
323 return true; }
325 let (Some(a), Some(b)) = (gv.data.checked_add(a), gv.data.checked_add(b)) else { return false };
326 if b > t.len() {
327 return false;
328 }
329 let g = Tb(&t.0[a as usize..b as usize]);
330 self.glyph_deltas(t, gv, g, coords, orig, ends, out, npts, budget).is_some()
331 }
332
333 #[allow(clippy::too_many_arguments)]
334 fn glyph_deltas(&self, t: Tb, gv: &Gvar, g: Tb, coords: &[i16], orig: &[(f32, f32)], ends: &[usize], out: &mut [(f32, f32)], npts: usize, budget: &mut u32) -> Option<()> {
335 let naxes = self.axes.len() as u32;
336 let tvc = r16(g, 0)?;
337 let count = tvc & 0x0FFF;
338 let mut p = r16(g, 2)?; let end = g.len();
340 let mut shared: Vec<u32> = Vec::new();
341 if tvc & SHARED_POINTS != 0 {
342 packed_points(g, &mut p, end, &mut shared)?;
343 }
344 let mut h = 4u32; let mut peak = vec![0i32; naxes as usize];
346 let mut inter = vec![(0i32, 0i32); naxes as usize];
347 let mut pts: Vec<u32> = Vec::new();
348 let mut dx: Vec<i32> = Vec::new();
349 let mut dy: Vec<i32> = Vec::new();
350 let mut tmp: Vec<(f32, f32)> = Vec::new();
351 let mut touched: Vec<bool> = Vec::new();
352 for _ in 0..count {
353 let size = r16(g, h)?;
354 let idx = r16(g, h + 2)?;
355 h += 4;
356 if idx & EMBEDDED_PEAK != 0 {
357 for (k, v) in peak.iter_mut().enumerate() {
358 *v = s16(g, h + k as u32 * 2)?;
359 }
360 h += naxes * 2;
361 } else {
362 let si = idx & 0x0FFF;
363 if si >= gv.nshared {
364 return None;
365 }
366 for (k, v) in peak.iter_mut().enumerate() {
367 *v = s16(t, gv.shared + (si * naxes + k as u32) * 2)?;
368 }
369 }
370 let has_inter = idx & INTERMEDIATE != 0;
371 if has_inter {
372 for (k, v) in inter.iter_mut().enumerate() {
373 *v = (s16(g, h + k as u32 * 2)?, s16(g, h + (naxes + k as u32) * 2)?);
374 }
375 h += naxes * 4;
376 }
377 let q0 = p;
378 let qend = p.checked_add(size)?;
379 if qend > end {
380 return None;
381 }
382 p = qend;
383 let scalar = tuple_scalar(coords, &peak, if has_inter { Some(&inter) } else { None });
384 if scalar == 0.0 {
385 continue;
386 }
387 let cost = (npts as u32).max(1);
389 if *budget < cost {
390 return None;
391 }
392 *budget -= cost;
393 let mut q = q0;
394 let all = if idx & PRIVATE_POINTS != 0 {
395 packed_points(g, &mut q, qend, &mut pts)?;
396 pts.is_empty()
397 } else {
398 pts.clear();
399 pts.extend_from_slice(&shared);
400 pts.is_empty()
401 };
402 let num = if all { npts } else { pts.len() };
403 packed_deltas(g, &mut q, qend, num, &mut dx)?;
404 packed_deltas(g, &mut q, qend, num, &mut dy)?;
405 let s = scalar as f32;
406 if all {
407 for (i, o) in out.iter_mut().enumerate() {
408 o.0 += dx[i] as f32 * s;
409 o.1 += dy[i] as f32 * s;
410 }
411 continue;
412 }
413 tmp.clear();
415 tmp.resize(npts, (0.0, 0.0));
416 touched.clear();
417 touched.resize(npts, false);
418 for (k, &pi) in pts.iter().enumerate() {
419 let pi = pi as usize;
420 if pi >= npts {
421 continue;
422 }
423 tmp[pi].0 += dx[k] as f32 * s;
424 tmp[pi].1 += dy[k] as f32 * s;
425 touched[pi] = true;
426 }
427 if !ends.is_empty() {
428 iup(orig, ends, &touched, &mut tmp);
429 }
430 for (o, d) in out.iter_mut().zip(&tmp) {
431 o.0 += d.0;
432 o.1 += d.1;
433 }
434 }
435 Some(())
436 }
437
438 pub(crate) fn hvar_delta(&self, t: Tb, glyph: u32, coords: &[i16]) -> Option<f32> {
442 let (store, map) = self.hvar;
443 if store == 0 {
444 return None;
445 }
446 let (outer, inner) = if map == 0 { (0, glyph) } else { dsim(t, map, glyph).unwrap_or((0, glyph)) };
447 Some(ivs(t, store, outer, inner, coords).unwrap_or(0.0))
448 }
449
450 pub(crate) fn mvar_delta(&self, t: Tb, tag: u32, coords: &[i16]) -> f32 {
452 match self.mvar.iter().find(|r| r.0 == tag) {
453 Some(&(_, o, i)) => ivs(t, self.mvar_store, o as u32, i as u32, coords).unwrap_or(0.0),
454 None => 0.0,
455 }
456 }
457
458 pub(crate) fn gdef_delta(&self, t: Tb, outer: u32, inner: u32, coords: &[i16]) -> f32 {
460 if self.gdef_store == 0 {
461 return 0.0;
462 }
463 ivs(t, self.gdef_store, outer, inner, coords).unwrap_or(0.0)
464 }
465}
466
467fn avar_map(seg: &[(i32, i32)], v: i32) -> i32 {
470 let fr = |k: usize| seg[k].0 << 2;
471 let to = |k: usize| seg[k].1 << 2;
472 match seg.len() {
473 0 => return v,
474 1 => return v - fr(0) + to(0),
475 _ => {}
476 }
477 if v <= fr(0) {
478 return v - fr(0) + to(0);
479 }
480 let count = seg.len() - 1;
481 let mut i = 1;
482 while i < count && v > fr(i) {
483 i += 1;
484 }
485 if v >= fr(i) {
486 return v - fr(i) + to(i);
487 }
488 if fr(i - 1) == fr(i) {
489 return to(i - 1);
490 }
491 let denom = (fr(i) - fr(i - 1)) as f32;
492 floor_i(to(i - 1) as f32 + ((to(i) - to(i - 1)) as f32 * (v - fr(i - 1)) as f32) / denom + 0.5)
493}
494
495fn tuple_scalar(coords: &[i16], peak: &[i32], inter: Option<&Vec<(i32, i32)>>) -> f64 {
497 let mut s = 1.0f64;
498 for (i, &pk) in peak.iter().enumerate() {
499 if pk == 0 {
500 continue;
501 }
502 let v = coords.get(i).copied().unwrap_or(0) as i32;
503 if v == 0 {
504 return 0.0;
505 }
506 if v == pk {
507 continue;
508 }
509 match inter {
510 Some(r) => {
511 let (st, en) = r[i];
512 if st > pk || pk > en || (st < 0 && en > 0) {
513 continue; }
515 if v < st || v > en {
516 return 0.0;
517 }
518 if v < pk {
519 if pk != st {
520 s *= (v - st) as f64 / (pk - st) as f64;
521 }
522 } else if pk != en {
523 s *= (en - v) as f64 / (en - pk) as f64;
524 }
525 }
526 None => {
527 if v < pk.min(0) || v > pk.max(0) {
528 return 0.0;
529 }
530 s *= v as f64 / pk as f64;
531 }
532 }
533 }
534 s
535}
536
537fn packed_points(t: Tb, p: &mut u32, end: u32, out: &mut Vec<u32>) -> Option<()> {
539 out.clear();
540 let rd = |p: &mut u32| -> Option<u32> {
541 if *p >= end {
542 return None;
543 }
544 let v = t.u8(*p);
545 *p += 1;
546 if v < 0 { None } else { Some(v as u32) }
547 };
548 let mut count = rd(p)?;
549 if count & 0x80 != 0 {
550 count = ((count & 0x7F) << 8) | rd(p)?;
551 }
552 let mut n = 0u32;
553 while (out.len() as u32) < count {
554 let ctrl = rd(p)?;
555 let run = (ctrl & 0x7F) + 1;
556 if out.len() as u32 + run > count {
557 return None;
558 }
559 for _ in 0..run {
560 let d = if ctrl & 0x80 != 0 { (rd(p)? << 8) | rd(p)? } else { rd(p)? };
561 n = n.wrapping_add(d);
562 out.push(n);
563 }
564 }
565 Some(())
566}
567
568fn packed_deltas(t: Tb, p: &mut u32, end: u32, n: usize, out: &mut Vec<i32>) -> Option<()> {
570 out.clear();
571 while out.len() < n {
572 if *p >= end {
573 return None;
574 }
575 let ctrl = t.u8(*p);
576 if ctrl < 0 {
577 return None;
578 }
579 *p += 1;
580 let ctrl = ctrl as u32;
581 let run = (ctrl & 0x3F) as usize + 1;
582 if out.len() + run > n {
583 return None;
584 }
585 let size = match ctrl & 0xC0 {
586 0x80 => 0,
587 0x40 => 2,
588 0xC0 => 4,
589 _ => 1,
590 };
591 if *p as u64 + (run * size) as u64 > end as u64 {
592 return None;
593 }
594 for _ in 0..run {
595 let v = match size {
596 0 => 0,
597 1 => t.u8(*p) as u8 as i8 as i32,
598 2 => t.u16(*p) as u16 as i16 as i32,
599 _ => t.u32(*p) as u32 as i32,
600 };
601 *p += size as u32;
602 out.push(v);
603 }
604 }
605 Some(())
606}
607
608fn iup(orig: &[(f32, f32)], ends: &[usize], touched: &[bool], d: &mut [(f32, f32)]) {
612 let mut start = 0usize;
613 for &e in ends {
614 if e >= orig.len() || e < start {
615 break;
616 }
617 let range = start..e + 1;
618 start = e + 1;
619 let first = match range.clone().find(|&i| touched[i]) {
620 Some(f) => f,
621 None => continue, };
623 let n = range.end - range.start;
625 let mut cur = first;
626 loop {
627 let mut nx = cur;
629 let mut gap = 0usize;
630 loop {
631 nx = if nx + 1 == range.end { range.start } else { nx + 1 };
632 if touched[nx] {
633 break;
634 }
635 gap += 1;
636 }
637 if gap > 0 {
638 let mut i = if cur + 1 == range.end { range.start } else { cur + 1 };
639 for _ in 0..gap {
640 let (x, y) = orig[i];
641 let px = iup_one(x, orig[cur].0, d[cur].0, orig[nx].0, d[nx].0);
642 let py = iup_one(y, orig[cur].1, d[cur].1, orig[nx].1, d[nx].1);
643 d[i] = (px, py);
644 i = if i + 1 == range.end { range.start } else { i + 1 };
645 }
646 }
647 if nx == first || n == 1 {
648 break;
649 }
650 cur = nx;
651 }
652 }
653}
654
655fn iup_one(c: f32, c1: f32, d1: f32, c2: f32, d2: f32) -> f32 {
657 let (c1, d1, c2, d2) = if c1 <= c2 { (c1, d1, c2, d2) } else { (c2, d2, c1, d1) };
658 if c1 == c2 {
659 return if d1 == d2 { d1 } else { 0.0 };
660 }
661 if c <= c1 {
662 d1
663 } else if c >= c2 {
664 d2
665 } else {
666 d1 + (c - c1) * (d2 - d1) / (c2 - c1)
667 }
668}
669
670fn dsim(t: Tb, m: u32, i: u32) -> Option<(u32, u32)> {
672 let fmt = t.u8(m);
673 let ef = t.u8(m.checked_add(1)?);
674 if ef < 0 {
675 return None;
676 }
677 let ef = ef as u32;
678 let (count, data) = match fmt {
679 0 => (r16(t, m + 2)?, m + 4),
680 1 => (r32(t, m + 2)?, m + 6),
681 _ => return None,
682 };
683 if count == 0 {
684 return Some((i >> 16, i & 0xFFFF));
685 }
686 let i = i.min(count - 1);
687 let size = ((ef >> 4) & 3) + 1;
688 let bits = (ef & 0xF) + 1;
689 let o = at(data, i, size)?;
690 let mut v = 0u32;
691 for k in 0..size {
692 let b = t.u8(o.checked_add(k)?);
693 if b < 0 {
694 return None;
695 }
696 v = (v << 8) | b as u32;
697 }
698 Some((v >> bits, v & ((1u32 << bits) - 1)))
699}
700
701fn region_factor(v: i32, start: i32, peak: i32, end: i32) -> f32 {
703 if peak == 0 || v == peak {
704 return 1.0;
705 }
706 if v == 0 {
707 return 0.0;
708 }
709 if start > peak || peak > end || (start < 0 && end > 0) {
710 return 1.0;
711 }
712 if v <= start || end <= v {
713 return 0.0;
714 }
715 if v < peak { (v - start) as f32 / (peak - start) as f32 } else { (end - v) as f32 / (end - peak) as f32 }
716}
717
718fn ivs_data(t: Tb, s: u32, outer: u32) -> Option<(u32, u32, u32)> {
721 if s == 0 || r16(t, s)? != 1 {
722 return None;
723 }
724 let rl = s.checked_add(r32(t, s.checked_add(2)?)?)?;
725 if outer >= r16(t, s.checked_add(6)?)? {
726 return None;
727 }
728 let d = s.checked_add(r32(t, at(s.checked_add(8)?, outer, 4)?)?)?;
729 Some((d, rl, r16(t, d.checked_add(4)?)?))
730}
731
732pub(crate) fn ivs_region_count(t: Tb, s: u32, outer: u32) -> Option<u32> {
735 Some(ivs_data(t, s, outer)?.2)
736}
737
738pub(crate) fn ivs_scalars(t: Tb, s: u32, outer: u32, coords: &[i16], max: usize, out: &mut Vec<f32>) -> Option<()> {
742 out.clear();
743 let (d, rl, rc) = ivs_data(t, s, outer)?;
744 if rc as usize > max {
745 return None;
746 }
747 let axes = r16(t, rl)?;
748 let nreg = r16(t, rl.checked_add(2)?)?;
749 if rc.saturating_mul(axes.max(1)) > IVS_WORK {
750 return None;
751 }
752 let zero = coords.iter().all(|&c| c == 0);
753 let recs = rl.checked_add(4)?;
754 for k in 0..rc {
755 let ri = r16(t, at(d.checked_add(6)?, k, 2)?)?;
756 if ri >= nreg {
757 return None;
758 }
759 let mut sc = if zero { 0.0f32 } else { 1.0 };
760 if !zero {
761 for a in 0..axes {
762 let rec = at(recs, ri * axes + a, 6)?;
763 let (st, pk, en) = (s16(t, rec)?, s16(t, rec.checked_add(2)?)?, s16(t, rec.checked_add(4)?)?);
764 let v = coords.get(a as usize).copied().unwrap_or(0) as i32;
765 let f = region_factor(v, st, pk, en);
766 if f == 0.0 {
767 sc = 0.0;
768 break;
769 }
770 sc *= f;
771 }
772 }
773 out.push(sc);
774 }
775 Some(())
776}
777
778fn ivs(t: Tb, s: u32, outer: u32, inner: u32, coords: &[i16]) -> Option<f32> {
780 if s == 0 || r16(t, s)? != 1 {
781 return None;
782 }
783 let rl = s.checked_add(r32(t, s + 2)?)?;
784 if outer >= r16(t, s + 6)? {
785 return None;
786 }
787 let d = s.checked_add(r32(t, at(s + 8, outer, 4)?)?)?;
788 let items = r16(t, d)?;
789 let wc = r16(t, d + 2)?;
790 let rc = r16(t, d + 4)?;
791 if inner >= items {
792 return None;
793 }
794 let long = wc & 0x8000 != 0;
795 let words = wc & 0x7FFF;
796 if words > rc {
797 return None;
798 }
799 let (ws, bs) = if long { (4u32, 2u32) } else { (2, 1) };
800 let row = words * ws + (rc - words) * bs;
801 let base = at(d.checked_add(6 + rc * 2)?, inner, row)?;
802 let axes = r16(t, rl)?;
803 let nreg = r16(t, rl + 2)?;
804 if rc.saturating_mul(axes.max(1)) > IVS_WORK {
805 return None;
806 }
807 let mut sum = 0.0f32;
808 for k in 0..rc {
809 let ri = r16(t, d + 6 + k * 2)?;
810 if ri >= nreg {
811 return None;
812 }
813 let mut sc = 1.0f32;
814 for a in 0..axes {
815 let rec = at(rl + 4, ri * axes + a, 6)?;
816 let (st, pk, en) = (s16(t, rec)?, s16(t, rec + 2)?, s16(t, rec + 4)?);
817 let v = coords.get(a as usize).copied().unwrap_or(0) as i32;
818 let f = region_factor(v, st, pk, en);
819 if f == 0.0 {
820 sc = 0.0;
821 break;
822 }
823 sc *= f;
824 }
825 if sc == 0.0 {
826 continue;
827 }
828 let dv = if k < words {
829 let o = at(base, k, ws)?;
830 if long { r32(t, o)? as i32 } else { s16(t, o)? }
831 } else {
832 let o = at(base.checked_add(words * ws)?, k - words, bs)?;
833 if long {
834 s16(t, o)?
835 } else {
836 let b = t.u8(o);
837 if b < 0 {
838 return None;
839 }
840 b as u8 as i8 as i32
841 }
842 };
843 sum += sc * dv as f32;
844 }
845 Some(sum)
846}