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)) if ch.len() >= 2 => (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            for slot in &mut ctx.globals.g00buf {
55                *slot = None;
56            }
57            for name in &mut ctx.globals.g00buf_names {
58                *name = None;
59            }
60            ctx.push(Value::Int(0));
61            return Ok(true);
62        }
63
64        if op == ctx.ids.elm_array {
65            if chain.len() < 4 {
66                ctx.push(Value::Int(0));
67                return Ok(true);
68            }
69            let idx_i32 = chain[2];
70            if idx_i32 < 0 {
71                ctx.push(Value::Int(0));
72                return Ok(true);
73            }
74            let idx = idx_i32 as usize;
75            ensure_buf_size(ctx, idx);
76            let buf_op = chain[3];
77
78            if ctx.ids.g00buf_load != 0 && buf_op == ctx.ids.g00buf_load {
79                let name = p_str(0);
80                match ctx.images.load_g00(name, 0) {
81                    Ok(img_id) => {
82                        ctx.globals.g00buf[idx] = Some(img_id);
83                        ctx.globals.g00buf_names[idx] = Some(name.to_string());
84                        ctx.push(Value::Int(0));
85                    }
86                    Err(_) => {
87                        ctx.globals.g00buf[idx] = None;
88                        ctx.globals.g00buf_names[idx] = None;
89                        ctx.push(Value::Int(0));
90                    }
91                }
92                return Ok(true);
93            }
94
95            if ctx.ids.g00buf_free != 0 && buf_op == ctx.ids.g00buf_free {
96                ctx.globals.g00buf[idx] = None;
97                ctx.globals.g00buf_names[idx] = None;
98                ctx.push(Value::Int(0));
99                return Ok(true);
100            }
101
102            let prop_key = composite_slot_op_key(idx_i32, buf_op);
103            prop_access::store_or_push_prop(ctx, form_id, prop_key, chain_pos.unwrap(), args);
104            return Ok(true);
105        }
106
107        prop_access::store_or_push_prop(ctx, form_id, op, chain_pos.unwrap(), args);
108        return Ok(true);
109    }
110
111    Ok(false)
112}