siglus_scene_vm/runtime/forms/
g00buf.rs1use anyhow::Result;
2
3use crate::runtime::forms::prop_access;
4use crate::runtime::{CommandContext, Value};
5
6const DEFAULT_G00BUF_CNT: usize = 64;
7
8fn configured_g00buf_cnt(ctx: &CommandContext) -> usize {
9 ctx.tables
10 .gameexe
11 .as_ref()
12 .map(|cfg| cfg.indexed_count("G00BUF"))
13 .filter(|n| *n > 0)
14 .unwrap_or(DEFAULT_G00BUF_CNT)
15}
16
17fn ensure_buf_size(ctx: &mut CommandContext, idx: usize) {
18 let want = (idx + 1).max(configured_g00buf_cnt(ctx));
19 if ctx.globals.g00buf.len() < want {
20 ctx.globals.g00buf.resize(want, None);
21 }
22 if ctx.globals.g00buf_names.len() < want {
23 ctx.globals.g00buf_names.resize(want, None);
24 }
25}
26
27fn composite_slot_op_key(idx: i32, op: i32) -> i32 {
28 ((idx & 0x7fff) << 16) ^ (op & 0xffff)
29}
30
31pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
32 let parsed = prop_access::parse_element_chain_ctx(ctx, form_id, args);
33 let (chain_pos, chain) = match parsed {
34 Some((pos, ch @ [_, _, ..])) => (Some(pos), Some(ch.to_vec())),
35 _ => (None, None),
36 };
37
38 if let Some(chain) = chain.as_ref() {
39 let op = chain[1];
40 let params = if let Some(pos) = chain_pos {
41 prop_access::script_args(args, pos)
42 } else {
43 &[]
44 };
45 let p_str = |i: usize| -> &str { params.get(i).and_then(|v| v.as_str()).unwrap_or("") };
46
47 if ctx.ids.g00buf_list_get_size != 0 && op == ctx.ids.g00buf_list_get_size {
48 let size = ctx.globals.g00buf.len().max(configured_g00buf_cnt(ctx));
49 ctx.push(Value::Int(size as i64));
50 return Ok(true);
51 }
52
53 if ctx.ids.g00buf_list_free_all != 0 && op == ctx.ids.g00buf_list_free_all {
54 ctx.globals.g00buf.fill(None);
55 ctx.globals.g00buf_names.fill(None);
56 ctx.push(Value::Int(0));
57 return Ok(true);
58 }
59
60 if op == ctx.ids.elm_array {
61 if chain.len() < 4 {
62 ctx.push(Value::Int(0));
63 return Ok(true);
64 }
65 let idx_i32 = chain[2];
66 if idx_i32 < 0 {
67 ctx.push(Value::Int(0));
68 return Ok(true);
69 }
70 let idx = idx_i32 as usize;
71 ensure_buf_size(ctx, idx);
72 let buf_op = chain[3];
73
74 if ctx.ids.g00buf_load != 0 && buf_op == ctx.ids.g00buf_load {
75 let name = p_str(0);
76 match ctx.images.load_g00(name, 0) {
77 Ok(img_id) => {
78 ctx.globals.g00buf[idx] = Some(img_id);
79 ctx.globals.g00buf_names[idx] = Some(name.to_string());
80 ctx.push(Value::Int(0));
81 }
82 Err(_) => {
83 ctx.globals.g00buf[idx] = None;
84 ctx.globals.g00buf_names[idx] = None;
85 ctx.push(Value::Int(0));
86 }
87 }
88 return Ok(true);
89 }
90
91 if ctx.ids.g00buf_free != 0 && buf_op == ctx.ids.g00buf_free {
92 ctx.globals.g00buf[idx] = None;
93 ctx.globals.g00buf_names[idx] = None;
94 ctx.push(Value::Int(0));
95 return Ok(true);
96 }
97
98 let prop_key = composite_slot_op_key(idx_i32, buf_op);
99 prop_access::store_or_push_prop(ctx, form_id, prop_key, chain_pos.unwrap(), args);
100 return Ok(true);
101 }
102
103 prop_access::store_or_push_prop(ctx, form_id, op, chain_pos.unwrap(), args);
104 return Ok(true);
105 }
106
107 Ok(false)
108}