siglus_scene_vm/runtime/forms/
excall.rs1use anyhow::{bail, Result};
2
3use crate::runtime::{CommandContext, Value};
4
5use super::codes::excall_op;
6use super::{counter, frame_action, frame_action_ch, int_list, script, stage};
7
8const EXCALL_LOCAL_NS_XOR: u32 = 0x4000;
9
10fn excall_form_key(ctx: &CommandContext) -> u32 {
11 if ctx.ids.form_global_excall != 0 {
12 ctx.ids.form_global_excall
13 } else {
14 super::codes::FORM_GLOBAL_EXCALL
15 }
16}
17
18fn stage_form_key(ctx: &CommandContext) -> u32 {
19 if ctx.ids.form_global_stage != 0 {
20 ctx.ids.form_global_stage
21 } else {
22 super::codes::FORM_GLOBAL_STAGE
23 }
24}
25
26fn script_form_key(ctx: &CommandContext) -> u32 {
27 if ctx.ids.form_global_script != 0 {
28 ctx.ids.form_global_script
29 } else {
30 0
31 }
32}
33
34fn excall_stage_form_key(ctx: &CommandContext, selector: i32) -> u32 {
35 let base = stage_form_key(ctx);
36 if selector == 0 {
37 base
38 } else {
39 base ^ EXCALL_LOCAL_NS_XOR
40 }
41}
42
43fn synth_form_key(base: u32, selector: i32, op: i32) -> u32 {
44 (base << 8) ^ (((selector as u32) & 0x0f) << 4) ^ (op as u32 & 0x0f)
45}
46
47fn parse_call<'a>(
48 ctx: &'a CommandContext,
49 args: &'a [Value],
50) -> Option<(
51 usize,
52 &'a [i32],
53 i32,
54 i32,
55 &'a [Value],
56 Option<i64>,
57 Option<i64>,
58)> {
59 let form_id = excall_form_key(ctx);
60 let (chain_pos, chain) = super::prop_access::parse_element_chain_ctx(ctx, form_id, args)?;
61 let (selector, op_pos) = if chain.len() >= 3
62 && chain[1] == crate::runtime::forms::codes::ELM_ARRAY
63 && (chain[2] == 0 || chain[2] == 1)
64 {
65 (chain[2], 3usize)
66 } else {
67 (1i32, 1usize)
68 };
69 let op = chain
70 .get(op_pos)
71 .copied()
72 .or_else(|| args.get(0).and_then(|v| v.as_i64()).map(|v| v as i32))?;
73 let params = if chain_pos > 1 {
74 &args[1..chain_pos]
75 } else {
76 &[]
77 };
78 let (meta_al_id, meta_ret_form) = crate::runtime::forms::prop_access::current_vm_meta(ctx);
79 let al_id = meta_al_id;
80 let ret_form = meta_ret_form;
81 Some((chain_pos, chain, selector, op, params, al_id, ret_form))
82}
83
84fn translated_call_args(
85 form_id: u32,
86 chain_tail: &[i32],
87 params: &[Value],
88 al_id: Option<i64>,
89 ret_form: Option<i64>,
90) -> Vec<Value> {
91 let mut out = Vec::new();
92 let op0 = chain_tail.first().copied().unwrap_or(0) as i64;
93 out.push(Value::Int(op0));
94 out.extend(params.iter().cloned());
95 let mut chain = Vec::with_capacity(1 + chain_tail.len());
96 chain.push(form_id as i32);
97 chain.extend_from_slice(chain_tail);
98 out.push(Value::Element(chain));
99 out.push(Value::Int(al_id.unwrap_or(0)));
100 out.push(Value::Int(ret_form.unwrap_or(0)));
101 out
102}
103
104fn translated_stage_args_to_form(
105 stage_form_id: u32,
106 stage_idx: Option<i32>,
107 chain_tail: &[i32],
108 params: &[Value],
109 al_id: Option<i64>,
110 ret_form: Option<i64>,
111) -> Vec<Value> {
112 let mut out = Vec::new();
113 let op0 = chain_tail.first().copied().unwrap_or(0) as i64;
114 out.push(Value::Int(op0));
115 out.extend(params.iter().cloned());
116
117 let mut chain = Vec::new();
118 chain.push(stage_form_id as i32);
119 if let Some(idx) = stage_idx {
120 chain.push(crate::runtime::forms::codes::ELM_ARRAY);
121 chain.push(idx);
122 }
123 chain.extend_from_slice(chain_tail);
124 out.push(Value::Element(chain));
125 out.push(Value::Int(al_id.unwrap_or(0)));
126 out.push(Value::Int(ret_form.unwrap_or(0)));
127 out
128}
129
130pub fn dispatch(ctx: &mut CommandContext, args: &[Value]) -> Result<bool> {
131 let Some((_chain_pos, chain, selector, op, params, al_id, ret_form)) = parse_call(ctx, args)
132 else {
133 if args.is_empty() {
134 bail!("EXCALL form expects at least one argument (op id)");
135 }
136 return Ok(false);
137 };
138
139 let op_pos = if chain.len() >= 3
140 && chain[1] == crate::runtime::forms::codes::ELM_ARRAY
141 && (chain[2] == 0 || chain[2] == 1)
142 {
143 3usize
144 } else {
145 1usize
146 };
147 let tail = chain.get(op_pos + 1..).unwrap_or(&[]);
148 let form_key = excall_form_key(ctx);
149
150 match op {
151 excall_op::OP_4 => {
152 ctx.excall_state.ready = true;
153 ctx.push(Value::Int(0));
154 }
155 excall_op::OP_5 => {
156 ctx.excall_state.ready = false;
157 ctx.push(Value::Int(0));
158 }
159 excall_op::OP_8 => {
160 ctx.push(Value::Int(if ctx.excall_state.ready { 1 } else { 0 }));
161 }
162 excall_op::OP_12 => {
163 ctx.push(Value::Int(if selector == 1 { 1 } else { 0 }));
164 }
165 excall_op::OP_0 => {
166 let forwarded = translated_stage_args_to_form(
167 excall_stage_form_key(ctx, selector),
168 None,
169 tail,
170 params,
171 al_id,
172 ret_form,
173 );
174 return stage::dispatch(ctx, &forwarded);
175 }
176 excall_op::OP_1 => {
177 let forwarded = translated_stage_args_to_form(
178 excall_stage_form_key(ctx, selector),
179 Some(0),
180 tail,
181 params,
182 al_id,
183 ret_form,
184 );
185 return stage::dispatch(ctx, &forwarded);
186 }
187 excall_op::OP_2 => {
188 let forwarded = translated_stage_args_to_form(
189 excall_stage_form_key(ctx, selector),
190 Some(1),
191 tail,
192 params,
193 al_id,
194 ret_form,
195 );
196 return stage::dispatch(ctx, &forwarded);
197 }
198 excall_op::OP_3 => {
199 let forwarded = translated_stage_args_to_form(
200 excall_stage_form_key(ctx, selector),
201 Some(2),
202 tail,
203 params,
204 al_id,
205 ret_form,
206 );
207 return stage::dispatch(ctx, &forwarded);
208 }
209 excall_op::OP_6 => {
210 let forwarded = translated_call_args(
211 synth_form_key(form_key, selector, op),
212 tail,
213 params,
214 al_id,
215 ret_form,
216 );
217 return counter::dispatch(ctx, synth_form_key(form_key, selector, op), &forwarded);
218 }
219 excall_op::OP_7 => {
220 let forwarded = translated_call_args(
221 synth_form_key(form_key, selector, op),
222 tail,
223 params,
224 al_id,
225 ret_form,
226 );
227 return int_list::dispatch(ctx, synth_form_key(form_key, selector, op), &forwarded);
228 }
229 excall_op::OP_9 => {
230 let forwarded = translated_call_args(
231 synth_form_key(form_key, selector, op),
232 tail,
233 params,
234 al_id,
235 ret_form,
236 );
237 return frame_action::dispatch(ctx, synth_form_key(form_key, selector, op), &forwarded);
238 }
239 excall_op::OP_10 => {
240 let forwarded = translated_call_args(
241 synth_form_key(form_key, selector, op),
242 tail,
243 params,
244 al_id,
245 ret_form,
246 );
247 return frame_action_ch::dispatch(
248 ctx,
249 synth_form_key(form_key, selector, op),
250 &forwarded,
251 );
252 }
253 excall_op::OP_13 => {
254 let script_form = script_form_key(ctx);
255 if script_form == 0 {
256 ctx.push(Value::Int(0));
257 return Ok(true);
258 }
259 let forwarded = translated_call_args(script_form, tail, params, al_id, ret_form);
260 return script::dispatch(ctx, script_form, &forwarded);
261 }
262 _ => {
263 let _ = form_key;
264 ctx.push(Value::Int(0));
265 }
266 }
267
268 Ok(true)
269}