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)) 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}