1use std::collections::{BTreeMap, BTreeSet};
17
18use crate::crs::{Reader, Writer, BLOCK_END, BLOCK_START};
19use crate::error::{format_err, Error, Result};
20use crate::savegame_schema::{schema_for, Cond, Op, ACHIEVEMENTS, TOWN};
21
22pub type ObjId = i32;
25
26#[derive(Debug, Clone, PartialEq)]
28pub enum Value {
29 Bool(bool),
31 I8(i8),
33 I16(i16),
35 U16(u16),
37 I32(i32),
39 U32(u32),
41 I64(i64),
43 F32(f32),
45 F32s(Vec<f32>),
47 I64s(Vec<i64>),
49 U32s(Vec<u32>),
51 Str(String),
53 Ext(u32),
55 Ptr(Option<ObjId>),
57 List(Vec<Fields>),
59 Block(u32, Fields),
61}
62
63#[derive(Debug, Clone, Default, PartialEq)]
65pub struct Fields(pub Vec<(&'static str, Value)>);
66
67impl Fields {
68 pub fn get(&self, name: &str) -> Option<&Value> {
70 self.0.iter().find(|(n, _)| *n == name).map(|(_, v)| v)
71 }
72
73 pub fn at(&self, path: &str) -> Option<&Value> {
75 let mut fields = self;
76 let mut parts = path.split('.').peekable();
77 while let Some(p) = parts.next() {
78 let v = fields.get(p)?;
79 if parts.peek().is_none() {
80 return Some(v);
81 }
82 match v {
83 Value::Block(_, f) => fields = f,
84 _ => return None,
85 }
86 }
87 None
88 }
89
90 pub fn at_mut(&mut self, path: &str) -> Option<&mut Value> {
92 let (head, rest) = match path.split_once('.') {
93 Some((h, r)) => (h, Some(r)),
94 None => (path, None),
95 };
96 let v = self
97 .0
98 .iter_mut()
99 .find(|(n, _)| *n == head)
100 .map(|(_, v)| v)?;
101 match rest {
102 None => Some(v),
103 Some(rest) => match v {
104 Value::Block(_, f) => f.at_mut(rest),
105 _ => None,
106 },
107 }
108 }
109
110 pub fn set(&mut self, path: &str, value: Value) -> bool {
114 let Some(slot) = self.at_mut(path) else {
115 return false;
116 };
117 if let (Value::I8(b), Value::Bool(v)) = (&*slot, &value) {
119 if (*b != 0) == *v {
120 return true;
121 }
122 }
123 *slot = value;
124 true
125 }
126
127 pub fn set_i32(&mut self, path: &str, v: i32) -> bool {
129 match self.at_mut(path) {
130 Some(Value::I32(x)) => *x = v,
131 Some(Value::U32(x)) | Some(Value::Ext(x)) => *x = v as u32,
132 Some(Value::I16(x)) => *x = v as i16,
133 Some(Value::I8(x)) => *x = v as i8,
134 Some(Value::U16(x)) => *x = v as u16,
135 _ => return false,
136 }
137 true
138 }
139
140 pub fn set_f32(&mut self, path: &str, v: f32) -> bool {
142 match self.at_mut(path) {
143 Some(Value::F32(x)) => *x = v,
144 _ => return false,
145 }
146 true
147 }
148
149 pub fn set_time(&mut self, path: &str, v: i64) -> bool {
151 match self.at_mut(path) {
152 Some(Value::I64(x)) => *x = v,
153 _ => return false,
154 }
155 true
156 }
157
158 pub fn set_bool(&mut self, path: &str, v: bool) -> bool {
160 self.set(path, Value::Bool(v))
161 }
162
163 pub fn i32(&self, path: &str) -> Option<i32> {
165 match self.at(path)? {
166 Value::I32(v) => Some(*v),
167 Value::I16(v) => Some(*v as i32),
168 Value::I8(v) => Some(*v as i32),
169 Value::U32(v) | Value::Ext(v) => Some(*v as i32),
170 _ => None,
171 }
172 }
173
174 pub fn f32(&self, path: &str) -> Option<f32> {
176 match self.at(path)? {
177 Value::F32(v) => Some(*v),
178 _ => None,
179 }
180 }
181
182 pub fn ptr(&self, path: &str) -> Option<ObjId> {
184 match self.at(path)? {
185 Value::Ptr(p) => *p,
186 _ => None,
187 }
188 }
189}
190
191#[derive(Debug, Clone)]
193pub struct Object {
194 pub index: ObjId,
196 pub tag: u32,
199 pub type_id: u32,
201 pub type_name: &'static str,
203 pub fields: Fields,
205}
206
207#[derive(Debug, Clone, Default)]
209pub struct GameState {
210 pub references: BTreeMap<u32, String>,
212 pub climate: u32,
215 pub start_condition: u32,
217 pub terrain: u32,
219 pub seed: u32,
221 pub size: i32,
223 pub flags: u32,
225 pub scenario: i32,
227 pub random_index: u32,
229 pub random_state: Vec<u32>,
231 pub town_name: String,
233 pub goal_done: bool,
236 pub goal: Option<ObjId>,
238 pub entity_time: i64,
243 pub entities: Vec<Option<ObjId>>,
245 pub buckets: Vec<Vec<Option<ObjId>>>,
247 pub bucket_values: [[i32; 5]; 2],
249 pub schedules: Vec<(i32, i32, i32, i64)>,
251 pub version: u32,
253 pub wide_strings: bool,
255 pub persist_state: Vec<u8>,
257 pub entity_manager_version: u32,
259 pub achievements: Fields,
261 pub animations: Vec<u8>,
264 pub audio_scene: Vec<u8>,
266 pub camera: Option<crate::menu_scene::SceneCamera>,
270 pub terrain_map: Option<crate::menu_scene::MapComponent>,
274 pub town: Fields,
278 pub map: GameMap,
282 pub managers: Fields,
285 pub objects: BTreeMap<ObjId, Object>,
287}
288
289#[derive(Debug, Clone, Default, PartialEq)]
293pub struct GameMapCell {
294 pub entities: [Option<ObjId>; 2],
297 pub description: u32,
300 pub flags: [u8; 9],
303 pub value: u16,
306 pub bytes: [u8; 6],
309}
310
311#[derive(Debug, Clone, Default)]
316pub struct GameMap {
317 pub size: i32,
319 pub cells: Vec<GameMapCell>,
321 pub trailer: [i16; 9],
323}
324
325struct Decoder<'a> {
326 r: Reader<'a>,
327 objects: BTreeMap<ObjId, Object>,
328 defining: BTreeSet<ObjId>,
329}
330
331fn err<T>(at: usize, msg: impl Into<String>) -> Result<T> {
332 Err(Error::Format(format!("{} at {at:#x}", msg.into())))
333}
334
335impl Decoder<'_> {
336 fn ptr(&mut self) -> Result<Option<ObjId>> {
337 if !self.r.bool()? {
338 return Ok(None);
339 }
340 let at = self.r.pos();
341 let index = self.r.i32()?;
342 let tag = self.r.u32()?;
343 let ty = self.r.u32()?;
344 if self.objects.contains_key(&index) || self.defining.contains(&index) {
345 return Ok(Some(index));
346 }
347 let Some((name, ops)) = schema_for(ty) else {
348 return Err(Error::Unsupported(format!(
349 "object type {ty:#010x} (index {index}) at {at:#x}"
350 )));
351 };
352 self.defining.insert(index);
353 let fields = self.ops(ops, 0).map_err(|e| match e {
354 Error::Format(m) => Error::Format(format!("{m} [in {name} at {at:#x}]")),
355 Error::Truncated { at: t, wanted } => Error::Format(format!(
356 "truncated: wanted {wanted} bytes at {t:#x} [in {name} at {at:#x}]"
357 )),
358 e => e,
359 })?;
360 self.defining.remove(&index);
361 self.objects.insert(
362 index,
363 Object {
364 index,
365 tag,
366 type_id: ty,
367 type_name: name,
368 fields,
369 },
370 );
371 Ok(Some(index))
372 }
373
374 fn ptrs(&mut self) -> Result<Vec<Option<ObjId>>> {
375 let n = self.r.count()?;
376 (0..n).map(|_| self.ptr()).collect()
377 }
378
379 fn block<T>(&mut self, f: impl FnOnce(&mut Self, u32) -> Result<T>) -> Result<T> {
381 let start = self.r.pos();
382 if self.r.u32()? != BLOCK_START {
383 return err(start, "expected a block");
384 }
385 let size = self.r.u32()? as usize;
386 let version = self.r.u32()?;
387 let end = self.r.pos() + size;
388 if size < 4 || self.r.u32_at(end - 4) != Some(BLOCK_END) {
389 return err(start, "block lacks its terminator");
390 }
391 let outer = self.r.limit(end - 4)?;
392 let result = f(self, version);
393 let unread = self.r.remaining();
394 self.r.unlimit(outer);
395 let value = result?;
396 if unread != 0 {
397 return err(
398 start,
399 format!("block (version {version}) has {unread} unread bytes"),
400 );
401 }
402 self.r.set_pos(end);
403 Ok(value)
404 }
405
406 fn game_map(&mut self) -> Result<GameMap> {
408 self.block(|d, version| {
409 let size = d.r.i32()?;
410 let n = d.r.count()?;
411 let mut cells = Vec::with_capacity(n);
412 for _ in 0..n {
413 let entities = [d.ptr()?, d.ptr()?];
414 let description = d.r.u32()?;
415 let flags = d.r.bytes(9)?.try_into().unwrap();
416 let value = d.r.u16()?;
417 if version < 3 {
419 d.r.u16()?;
420 }
421 let bytes = d.r.bytes(6)?.try_into().unwrap();
422 cells.push(GameMapCell {
423 entities,
424 description,
425 flags,
426 value,
427 bytes,
428 });
429 }
430 let mut trailer = [0; 9];
431 for t in &mut trailer {
432 *t = d.r.i16()?;
433 }
434 Ok(GameMap {
435 size,
436 cells,
437 trailer,
438 })
439 })
440 }
441
442 fn raw_block(&mut self) -> Result<Vec<u8>> {
444 let start = self.r.pos();
445 self.skip_block()?;
446 Ok(self.r.slice(start, self.r.pos())?.to_vec())
447 }
448
449 fn skip_block(&mut self) -> Result<()> {
450 self.block(|d, _| {
451 d.r.skip_rest();
452 Ok(())
453 })
454 }
455
456 fn ops(&mut self, ops: &[Op], version: u32) -> Result<Fields> {
457 let mut out = Fields::default();
458 self.ops_into(ops, version, &mut out)?;
459 Ok(out)
460 }
461
462 fn ops_into(&mut self, ops: &[Op], version: u32, out: &mut Fields) -> Result<()> {
463 for op in ops {
464 let (name, value) = match *op {
465 Op::Bool(n) => match self.r.u8()? {
467 b @ (0 | 1) => (n, Value::Bool(b == 1)),
468 b => (n, Value::I8(b as i8)),
469 },
470 Op::I8(n) => (n, Value::I8(self.r.u8()? as i8)),
471 Op::I16(n) => (n, Value::I16(self.r.i16()?)),
472 Op::U16(n) => (n, Value::U16(self.r.u16()?)),
473 Op::I32(n) => (n, Value::I32(self.r.i32()?)),
474 Op::U32(n) => (n, Value::U32(self.r.u32()?)),
475 Op::Time(n) => (n, Value::I64(self.r.i64()?)),
476 Op::F32(n) => (n, Value::F32(self.r.f32()?)),
477 Op::Times(n, k) => (
478 n,
479 Value::I64s((0..k).map(|_| self.r.i64()).collect::<Result<_>>()?),
480 ),
481 Op::F32s(n, k) => (
482 n,
483 Value::F32s((0..k).map(|_| self.r.f32()).collect::<Result<_>>()?),
484 ),
485 Op::U32s(n, k) => (
486 n,
487 Value::U32s((0..k).map(|_| self.r.u32()).collect::<Result<_>>()?),
488 ),
489 Op::I32sBy(n, by) => {
490 let k = out.i32(by).unwrap_or(0).max(0) as usize;
491 (
492 n,
493 Value::U32s((0..k).map(|_| self.r.u32()).collect::<Result<_>>()?),
494 )
495 }
496 Op::F32sBy(n, by) => {
497 let k = out.i32(by).unwrap_or(0).max(0) as usize;
498 (
499 n,
500 Value::F32s((0..k).map(|_| self.r.f32()).collect::<Result<_>>()?),
501 )
502 }
503 Op::Str(n) => (n, Value::Str(self.r.string()?)),
504 Op::WStr(n) => (n, Value::Str(self.r.wstring()?)),
505 Op::Ext(n) => (n, Value::Ext(self.r.u32()?)),
506 Op::Ptr(n) => (n, Value::Ptr(self.ptr()?)),
507 Op::List(n, item) => {
508 let count = self.r.count()?;
509 let mut items = Vec::with_capacity(count.min(1 << 16));
510 for _ in 0..count {
511 items.push(self.ops(item, version)?);
512 }
513 (n, Value::List(items))
514 }
515 Op::Rest(n, item) => {
516 let mut items = Vec::new();
517 while self.r.remaining() > 0 {
518 items.push(self.ops(item, version)?);
519 }
520 (n, Value::List(items))
521 }
522 Op::Block(n, _, inner) => {
523 let (v, f) = self.block(|d, v| Ok((v, d.ops(inner, v)?)))?;
524 (n, Value::Block(v, f))
525 }
526 Op::Sub(inner) => {
527 self.ops_into(inner, version, out)?;
528 continue;
529 }
530 Op::If(cond, inner) => {
531 let take = match cond {
532 Cond::VersionAtLeast(v) => version >= v,
533 Cond::VersionBelow(v) => version < v,
534 Cond::Flag(f) => truthy(out.get(f)),
535 };
536 if take {
537 self.ops_into(inner, version, out)?;
538 }
539 continue;
540 }
541 };
542 out.0.push((name, value));
543 }
544 Ok(())
545 }
546}
547
548impl GameState {
549 pub fn new_object(&mut self, name: &'static str) -> Option<ObjId> {
554 let fields = crate::savegame_schema::default_fields(name)?;
555 let index = self.objects.keys().next_back().map_or(1, |&k| k + 1);
556 self.objects.insert(
557 index,
558 Object {
559 index,
560 tag: index as u32,
561 type_id: crate::crc::type_id(name),
562 type_name: name,
563 fields,
564 },
565 );
566 Some(index)
567 }
568
569 pub fn decode(state: &[u8]) -> Result<GameState> {
571 let mut d = Decoder {
572 r: Reader::new(state),
573 objects: BTreeMap::new(),
574 defining: BTreeSet::new(),
575 };
576 let mut g = GameState::default();
577 d.block(|d, version| {
578 g.version = version;
579 g.references = d.block(|d, _| {
580 let n = d.r.count()?;
581 (0..n)
582 .map(|_| Ok((d.r.u32()?, d.r.string()?)))
583 .collect::<Result<BTreeMap<_, _>>>()
584 })?;
585 d.r.set_reference_ids(std::sync::Arc::new(g.references.clone()));
586 g.persist_state = d.raw_block()?;
587 g.climate = d.r.u32()?;
588 g.start_condition = d.r.u32()?;
589 g.terrain = d.r.u32()?;
590 g.seed = d.r.u32()?;
591 g.size = d.r.i32()?;
592 if version > 0 {
593 g.flags = d.r.u32()?;
594 }
595 if version > 2 {
596 g.scenario = d.r.i32()?;
597 }
598 g.random_index = d.r.u32()?;
599 g.random_state = (0..0x270).map(|_| d.r.u32()).collect::<Result<_>>()?;
600 g.town_name = d.r.string()?;
601 g.goal_done = d.r.bool()?;
602 g.goal = d.ptr()?;
603 d.ops_into(&[ACHIEVEMENTS], 0, &mut g.achievements)?;
606 g.animations = d.raw_block()?;
607 g.audio_scene = d.raw_block()?;
608 g.camera = Some(crate::menu_scene::read_camera(&mut d.r)?);
609 d.block(|d, version| {
610 g.entity_manager_version = version;
611 g.entity_time = d.r.i64()?;
612 g.entities = d.ptrs()?;
613 g.buckets = (0..5).map(|_| d.ptrs()).collect::<Result<_>>()?;
614 for k in 0..2 {
615 for v in 0..5 {
616 g.bucket_values[k][v] = d.r.i32()?;
617 }
618 }
619 g.schedules = (0..g.entities.len())
620 .map(|_| Ok((d.r.i32()?, d.r.i32()?, d.r.i32()?, d.r.i64()?)))
621 .collect::<Result<_>>()?;
622 Ok(())
623 })?;
624 g.terrain_map = Some(crate::menu_scene::read_map_component(&mut d.r)?);
625 d.ops_into(&[TOWN], 0, &mut g.town)?;
626 g.map = d.game_map()?;
627 d.ops_into(&managers(version), 0, &mut g.managers)?;
628 Ok(())
629 })?;
630 g.objects = d.objects;
631 g.wide_strings = d.r.wide_seen;
632 Ok(g)
633 }
634
635 pub fn encode(&self) -> Result<Vec<u8>> {
638 let mut e = Encoder {
639 w: Writer::new(),
640 objects: &self.objects,
641 written: BTreeSet::new(),
642 };
643 e.w.wide_strings = self.wide_strings;
644 let at = e.w.begin_block(self.version);
645 let refs = e.w.begin_block(0);
646 e.w.count(self.references.len());
647 for (&id, path) in &self.references {
648 e.w.u32(id);
649 e.w.string(path);
650 }
651 e.w.end_block(refs);
652 e.w.set_reference_ids(&self.references);
653 let slots = self
656 .objects
657 .keys()
658 .max()
659 .map_or(0, |&m| m as u32 + 1)
660 .max(persist_slots(&self.persist_state));
661 let at_persist = e.w.begin_block(0);
662 e.w.u32(slots);
663 e.w.end_block(at_persist);
664 e.w.u32(self.climate);
665 e.w.u32(self.start_condition);
666 e.w.u32(self.terrain);
667 e.w.u32(self.seed);
668 e.w.i32(self.size);
669 if self.version > 0 {
670 e.w.u32(self.flags);
671 }
672 if self.version > 2 {
673 e.w.i32(self.scenario);
674 }
675 e.w.u32(self.random_index);
676 self.random_state.iter().for_each(|&v| e.w.u32(v));
677 e.w.string(&self.town_name);
678 e.w.bool(self.goal_done);
679 e.ptr(self.goal)?;
680 e.ops(&[ACHIEVEMENTS], 0, &self.achievements)?;
681 if self.animations.is_empty() {
682 e.w.bytes(&empty_animations());
683 } else {
684 e.w.bytes(&self.animations);
685 }
686 if self.audio_scene.is_empty() {
687 e.w.bytes(&empty_audio_scene());
688 } else {
689 e.w.bytes(&self.audio_scene);
690 }
691 let camera = self
692 .camera
693 .as_ref()
694 .ok_or_else(|| Error::Format("no camera".into()))?;
695 crate::menu_scene::write_camera(&mut e.w, camera)?;
696 let em = e.w.begin_block(self.entity_manager_version);
697 e.w.i64(self.entity_time);
698 e.ptrs(&self.entities)?;
699 for b in &self.buckets {
700 e.ptrs(b)?;
701 }
702 self.bucket_values
703 .iter()
704 .flatten()
705 .for_each(|&v| e.w.i32(v));
706 for &(a, b, c, t) in &self.schedules {
707 e.w.i32(a);
708 e.w.i32(b);
709 e.w.i32(c);
710 e.w.i64(t);
711 }
712 e.w.end_block(em);
713 let terrain = self
714 .terrain_map
715 .as_ref()
716 .ok_or_else(|| Error::Format("no terrain".into()))?;
717 crate::menu_scene::write_map_component(&mut e.w, terrain)?;
718 e.ops(&[TOWN], 0, &self.town)?;
719 e.game_map(&self.map)?;
720 e.ops(&managers(self.version), 0, &self.managers)?;
721 e.w.end_block(at);
722 Ok(e.w.data)
723 }
724}
725
726fn truthy(v: Option<&Value>) -> bool {
728 matches!(v, Some(Value::Bool(true))) || matches!(v, Some(Value::I8(b)) if *b != 0)
729}
730
731fn persist_slots(block: &[u8]) -> u32 {
734 block
735 .get(12..16)
736 .map_or(0, |b| u32::from_le_bytes(b.try_into().unwrap()))
737}
738
739pub fn empty_animations() -> Vec<u8> {
743 let mut w = Writer::new();
744 let at = w.begin_block(0);
745 w.u32(0);
746 w.end_block(at);
747 w.data
748}
749
750pub fn empty_audio_scene() -> Vec<u8> {
757 let mut w = Writer::new();
758 let at = w.begin_block(0);
759 w.u32(0x1000);
760 for _ in 0..0x1000 {
761 (0..3).for_each(|_| w.f32(0.0));
762 w.u32(0);
763 w.u32(u32::MAX);
764 w.i64(0);
765 w.u32(0);
766 w.f32(1.0);
767 w.u32(0);
768 }
769 w.bytes(&[0, 0, 1, 1, 1]);
770 w.end_block(at);
771 w.data
772}
773
774pub fn managers(version: u32) -> Vec<Op> {
777 use crate::savegame_schema::{CALENDAR, ENVIRONMENT, EVENT_LOG, JOBS, MINIMAP, TOOLS};
778 let mut ops = vec![JOBS, TOOLS, CALENDAR];
779 if version > 1 {
780 ops.push(EVENT_LOG);
781 }
782 ops.extend([ENVIRONMENT, MINIMAP]);
783 ops
784}
785
786struct Encoder<'a> {
787 w: Writer,
788 objects: &'a BTreeMap<ObjId, Object>,
789 written: BTreeSet<ObjId>,
790}
791
792impl Encoder<'_> {
793 fn ptr(&mut self, p: Option<ObjId>) -> Result<()> {
794 let Some(index) = p else {
795 self.w.bool(false);
796 return Ok(());
797 };
798 let o = self
799 .objects
800 .get(&index)
801 .ok_or_else(|| Error::Format(format!("object {index} is not defined")))?;
802 self.w.bool(true);
803 self.w.i32(index);
804 self.w.u32(o.tag);
805 self.w.u32(o.type_id);
806 if self.written.insert(index) {
807 let (_, ops) = schema_for(o.type_id)
808 .ok_or_else(|| Error::Unsupported(format!("object type {:#010x}", o.type_id)))?;
809 self.ops(ops, 0, &o.fields)?;
810 }
811 Ok(())
812 }
813
814 fn ptrs(&mut self, ptrs: &[Option<ObjId>]) -> Result<()> {
815 self.w.count(ptrs.len());
816 ptrs.iter().try_for_each(|&p| self.ptr(p))
817 }
818
819 fn game_map(&mut self, map: &GameMap) -> Result<()> {
820 let at = self.w.begin_block(4);
821 self.w.i32(map.size);
822 self.w.count(map.cells.len());
823 for c in &map.cells {
824 self.ptr(c.entities[0])?;
825 self.ptr(c.entities[1])?;
826 self.w.u32(c.description);
827 self.w.bytes(&c.flags);
828 self.w.u16(c.value);
829 self.w.bytes(&c.bytes);
830 }
831 map.trailer.iter().for_each(|&t| self.w.i16(t));
832 self.w.end_block(at);
833 Ok(())
834 }
835
836 fn ops(&mut self, ops: &[Op], version: u32, fields: &Fields) -> Result<()> {
837 let mut next = fields.0.iter();
838 self.ops_from(ops, version, fields, &mut next)?;
839 match next.next() {
840 None => Ok(()),
841 Some((n, _)) => format_err(format!("field {n} has no step to write it")),
842 }
843 }
844
845 fn ops_from<'f>(
847 &mut self,
848 ops: &[Op],
849 version: u32,
850 fields: &'f Fields,
851 next: &mut std::slice::Iter<'f, (&'static str, Value)>,
852 ) -> Result<()> {
853 for op in ops {
854 let name = match *op {
855 Op::Sub(inner) => {
856 self.ops_from(inner, version, fields, next)?;
857 continue;
858 }
859 Op::If(cond, inner) => {
860 let take = match cond {
861 Cond::VersionAtLeast(v) => version >= v,
862 Cond::VersionBelow(v) => version < v,
863 Cond::Flag(f) => truthy(fields.get(f)),
864 };
865 if take {
866 self.ops_from(inner, version, fields, next)?;
867 }
868 continue;
869 }
870 Op::Bool(n) | Op::I8(n) | Op::I16(n) | Op::U16(n) | Op::I32(n) | Op::U32(n) => n,
871 Op::Time(n) | Op::F32(n) | Op::Times(n, _) | Op::F32s(n, _) | Op::U32s(n, _) => n,
872 Op::I32sBy(n, _) | Op::F32sBy(n, _) | Op::Str(n) | Op::WStr(n) | Op::Ext(n) => n,
873 Op::Ptr(n) | Op::List(n, _) | Op::Rest(n, _) | Op::Block(n, _, _) => n,
874 };
875 let Some((field, value)) = next.next() else {
876 return format_err(format!("missing field {name}"));
877 };
878 if *field != name {
879 return format_err(format!("expected field {name}, found {field}"));
880 }
881 let w = &mut self.w;
882 match (*op, value) {
883 (Op::Bool(_), Value::Bool(v)) => w.bool(*v),
884 (Op::Bool(_), Value::I8(v)) => w.u8(*v as u8),
885 (Op::I8(_), Value::I8(v)) => w.u8(*v as u8),
886 (Op::I16(_), Value::I16(v)) => w.i16(*v),
887 (Op::U16(_), Value::U16(v)) => w.u16(*v),
888 (Op::I32(_), Value::I32(v)) => w.i32(*v),
889 (Op::U32(_), Value::U32(v)) | (Op::Ext(_), Value::Ext(v)) => w.u32(*v),
890 (Op::Time(_), Value::I64(v)) => w.i64(*v),
891 (Op::F32(_), Value::F32(v)) => w.f32(*v),
892 (Op::Times(_, _), Value::I64s(v)) => v.iter().for_each(|&t| w.i64(t)),
893 (Op::F32s(_, _) | Op::F32sBy(_, _), Value::F32s(v)) => {
894 v.iter().for_each(|&f| w.f32(f))
895 }
896 (Op::U32s(_, _) | Op::I32sBy(_, _), Value::U32s(v)) => {
897 v.iter().for_each(|&u| w.u32(u))
898 }
899 (Op::Str(_), Value::Str(v)) => w.string(v),
900 (Op::WStr(_), Value::Str(v)) => w.wstring(v),
901 (Op::Ptr(_), Value::Ptr(p)) => self.ptr(*p)?,
902 (Op::List(_, item), Value::List(items)) => {
903 w.count(items.len());
904 for f in items {
905 self.ops(item, version, f)?;
906 }
907 }
908 (Op::Rest(_, item), Value::List(items)) => {
909 for f in items {
910 self.ops(item, version, f)?;
911 }
912 }
913 (Op::Block(_, _, inner), Value::Block(v, f)) => {
914 let at = w.begin_block(*v);
915 self.ops(inner, *v, f)?;
916 self.w.end_block(at);
917 }
918 (op, value) => return format_err(format!("{op:?} cannot write {value:?}")),
919 }
920 }
921 Ok(())
922 }
923}