Skip to main content

siglus_scene_vm/runtime/forms/
g00buf.rs

1use 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}