siglus_scene_vm/runtime/forms/
database.rs1use anyhow::Result;
2
3use crate::runtime::forms::prop_access;
4use crate::runtime::{CommandContext, Value};
5
6fn composite_db_op_key(db_no: i32, op: i32) -> i32 {
7 ((db_no & 0x7fff) << 16) ^ (op & 0xffff)
8}
9
10fn ref_chain_from_value(v: &Value) -> Option<(Option<i32>, &[i32])> {
11 match v {
12 Value::Element(chain) => Some((None, chain.as_slice())),
13 Value::NamedArg { id, value } => match value.as_ref() {
14 Value::Element(chain) => Some((Some(*id), chain.as_slice())),
15 _ => None,
16 },
17 _ => None,
18 }
19}
20
21fn database_get_data(
22 ctx: &mut CommandContext,
23 db: &siglus_assets::dbs::DbsDatabase,
24 item_call_no: i32,
25 refs: &[Value],
26) {
27 let mut positional_column_no = 0i32;
28 for arg in refs {
29 let Some((explicit_column_no, chain)) = ref_chain_from_value(arg) else {
30 positional_column_no += 1;
31 continue;
32 };
33 let column_call_no = explicit_column_no.unwrap_or(positional_column_no);
34 positional_column_no += 1;
35
36 match db.check_column_no(column_call_no) {
37 1 => {
38 if let Ok(Some(v)) = db.get_data_int(item_call_no, column_call_no) {
39 prop_access::assign_to_chain(ctx, chain, Value::Int(v as i64));
40 } else {
41 prop_access::assign_to_chain(ctx, chain, Value::Int(0));
42 }
43 }
44 2 => {
45 if let Ok(Some(v)) = db.get_data_str(item_call_no, column_call_no) {
46 prop_access::assign_to_chain(ctx, chain, Value::Str(v));
47 } else {
48 prop_access::assign_to_chain(ctx, chain, Value::Str(String::new()));
49 }
50 }
51 _ => {
52 prop_access::assign_to_chain(ctx, chain, Value::Int(0));
53 }
54 }
55 }
56}
57
58fn push_unloaded_database_result(ctx: &mut CommandContext, db_op: i32) {
59 if ctx.ids.database_get_str != 0 && db_op == ctx.ids.database_get_str {
60 ctx.push(Value::Str(String::new()));
61 return;
62 }
63
64 if ctx.ids.database_get_data != 0 && db_op == ctx.ids.database_get_data {
65 return;
68 }
69
70 if (ctx.ids.database_find_num != 0 && db_op == ctx.ids.database_find_num)
71 || (ctx.ids.database_find_str != 0 && db_op == ctx.ids.database_find_str)
72 || (ctx.ids.database_find_str_real != 0 && db_op == ctx.ids.database_find_str_real)
73 {
74 ctx.push(Value::Int(-1));
75 return;
76 }
77
78 ctx.push(Value::Int(0));
80}
81
82pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
83 let parsed = prop_access::parse_element_chain_ctx(ctx, form_id, args);
84 let (chain_pos, chain) = match parsed {
85 Some((pos, ch @ [_, _, ..])) => (Some(pos), Some(ch)),
86 _ => (None, None),
87 };
88
89 if let Some(chain) = chain {
90 let op = chain[1];
91 let params = if let Some(pos) = chain_pos {
92 prop_access::script_args(args, pos)
93 } else {
94 &[]
95 };
96 let p_i32 =
97 |i: usize| -> i32 { params.get(i).and_then(|v| v.as_i64()).unwrap_or(0) as i32 };
98 let p_str = |i: usize| -> &str { params.get(i).and_then(|v| v.as_str()).unwrap_or("") };
99
100 if ctx.ids.database_list_get_size != 0 && op == ctx.ids.database_list_get_size {
101 if ctx.globals.database_off {
102 ctx.push(Value::Int(0));
103 } else {
104 ctx.push(Value::Int(ctx.tables.databases.len() as i64));
105 }
106 return Ok(true);
107 }
108
109 if op == ctx.ids.elm_array {
110 if chain.len() < 4 {
111 ctx.push(Value::Int(0));
112 return Ok(true);
113 }
114
115 let db_no = chain[2];
116 let db_op = chain[3];
117 if db_no < 0 {
118 push_unloaded_database_result(ctx, db_op);
119 return Ok(true);
120 }
121
122 if ctx.globals.database_off {
123 push_unloaded_database_result(ctx, db_op);
124 return Ok(true);
125 }
126
127 let db = match ctx
128 .tables
129 .databases
130 .get(db_no as usize)
131 .and_then(|db| db.clone())
132 {
133 Some(db) => db,
134 None => {
135 push_unloaded_database_result(ctx, db_op);
136 return Ok(true);
137 }
138 };
139
140 if db_op == ctx.ids.database_get_num {
141 let item_call_no = p_i32(0);
142 let col_call_no = p_i32(1);
143 let mut out: i64 = 0;
144 if let Ok(Some(v)) = db.get_data_int(item_call_no, col_call_no) {
145 out = v as i64;
146 }
147 ctx.push(Value::Int(out));
148 return Ok(true);
149 }
150
151 if ctx.ids.database_get_str != 0 && db_op == ctx.ids.database_get_str {
152 let item_call_no = p_i32(0);
153 let col_call_no = p_i32(1);
154 let mut out = String::new();
155 if let Ok(Some(v)) = db.get_data_str(item_call_no, col_call_no) {
156 out = v;
157 }
158 ctx.push(Value::Str(out));
159 return Ok(true);
160 }
161
162 if ctx.ids.database_get_data != 0 && db_op == ctx.ids.database_get_data {
163 let item_call_no = p_i32(0);
164 let refs = if params.len() > 1 { ¶ms[1..] } else { &[] };
165 database_get_data(ctx, &db, item_call_no, refs);
166 return Ok(true);
167 }
168
169 if ctx.ids.database_check_item != 0 && db_op == ctx.ids.database_check_item {
170 let item_call_no = p_i32(0);
171 ctx.push(Value::Int(db.check_item_no(item_call_no) as i64));
172 return Ok(true);
173 }
174
175 if ctx.ids.database_check_column != 0 && db_op == ctx.ids.database_check_column {
176 let col_call_no = p_i32(0);
177 ctx.push(Value::Int(db.check_column_no(col_call_no) as i64));
178 return Ok(true);
179 }
180
181 if ctx.ids.database_find_num != 0 && db_op == ctx.ids.database_find_num {
182 let col_call_no = p_i32(0);
183 let value = p_i32(1);
184 let out = db.find_num(col_call_no, value).unwrap_or(-1);
185 ctx.push(Value::Int(out as i64));
186 return Ok(true);
187 }
188
189 if ctx.ids.database_find_str != 0 && db_op == ctx.ids.database_find_str {
190 let col_call_no = p_i32(0);
191 let needle = p_str(1);
192 let out = db.find_str(col_call_no, needle).unwrap_or(-1);
193 ctx.push(Value::Int(out as i64));
194 return Ok(true);
195 }
196
197 if ctx.ids.database_find_str_real != 0 && db_op == ctx.ids.database_find_str_real {
198 let col_call_no = p_i32(0);
199 let needle = p_str(1);
200 let out = db.find_str_real(col_call_no, needle).unwrap_or(-1);
201 ctx.push(Value::Int(out as i64));
202 return Ok(true);
203 }
204
205 let prop_key = composite_db_op_key(db_no, db_op);
206 prop_access::store_or_push_prop(ctx, form_id, prop_key, chain_pos.unwrap(), args);
207 return Ok(true);
208 }
209
210 prop_access::store_or_push_prop(ctx, form_id, op, chain_pos.unwrap(), args);
211 return Ok(true);
212 }
213
214 Ok(false)
215}