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
58pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
59 let parsed = prop_access::parse_element_chain_ctx(ctx, form_id, args);
60 let (chain_pos, chain) = match parsed {
61 Some((pos, ch)) if ch.len() >= 2 => (Some(pos), Some(ch)),
62 _ => (None, None),
63 };
64
65 if let Some(chain) = chain {
66 let op = chain[1];
67 let params = if let Some(pos) = chain_pos {
68 prop_access::script_args(args, pos)
69 } else {
70 &[]
71 };
72 let p_i32 =
73 |i: usize| -> i32 { params.get(i).and_then(|v| v.as_i64()).unwrap_or(0) as i32 };
74 let p_str = |i: usize| -> &str { params.get(i).and_then(|v| v.as_str()).unwrap_or("") };
75
76 if ctx.ids.database_list_get_size != 0 && op == ctx.ids.database_list_get_size {
77 if ctx.globals.database_off {
78 ctx.push(Value::Int(0));
79 } else {
80 ctx.push(Value::Int(ctx.tables.databases.len() as i64));
81 }
82 return Ok(true);
83 }
84
85 if op == ctx.ids.elm_array {
86 if chain.len() < 4 {
87 ctx.push(Value::Int(0));
88 return Ok(true);
89 }
90
91 let db_no = chain[2];
92 if db_no < 0 {
93 ctx.push(Value::Int(0));
94 return Ok(true);
95 }
96
97 if ctx.globals.database_off {
98 let db_op = chain[3];
99 if ctx.ids.database_get_str != 0 && db_op == ctx.ids.database_get_str {
100 ctx.push(Value::Str(String::new()));
101 } else {
102 ctx.push(Value::Int(0));
103 }
104 return Ok(true);
105 }
106
107 let db = match ctx.tables.databases.get(db_no as usize).cloned() {
108 Some(db) => db,
109 None => {
110 ctx.push(Value::Int(0));
111 return Ok(true);
112 }
113 };
114
115 let db_op = chain[3];
116
117 if db_op == ctx.ids.database_get_num {
118 let item_call_no = p_i32(0);
119 let col_call_no = p_i32(1);
120 let mut out: i64 = 0;
121 if let Ok(Some(v)) = db.get_data_int(item_call_no, col_call_no) {
122 out = v as i64;
123 }
124 ctx.push(Value::Int(out));
125 return Ok(true);
126 }
127
128 if ctx.ids.database_get_str != 0 && db_op == ctx.ids.database_get_str {
129 let item_call_no = p_i32(0);
130 let col_call_no = p_i32(1);
131 let mut out = String::new();
132 if let Ok(Some(v)) = db.get_data_str(item_call_no, col_call_no) {
133 out = v;
134 }
135 ctx.push(Value::Str(out));
136 return Ok(true);
137 }
138
139 if ctx.ids.database_get_data != 0 && db_op == ctx.ids.database_get_data {
140 let item_call_no = p_i32(0);
141 let refs = if params.len() > 1 { ¶ms[1..] } else { &[] };
142 database_get_data(ctx, &db, item_call_no, refs);
143 ctx.push(Value::Int(0));
144 return Ok(true);
145 }
146
147 if ctx.ids.database_check_item != 0 && db_op == ctx.ids.database_check_item {
148 let item_call_no = p_i32(0);
149 ctx.push(Value::Int(db.check_item_no(item_call_no) as i64));
150 return Ok(true);
151 }
152
153 if ctx.ids.database_check_column != 0 && db_op == ctx.ids.database_check_column {
154 let col_call_no = p_i32(0);
155 ctx.push(Value::Int(db.check_column_no(col_call_no) as i64));
156 return Ok(true);
157 }
158
159 if ctx.ids.database_find_num != 0 && db_op == ctx.ids.database_find_num {
160 let col_call_no = p_i32(0);
161 let value = p_i32(1);
162 let out = db.find_num(col_call_no, value).unwrap_or(-1);
163 ctx.push(Value::Int(out as i64));
164 return Ok(true);
165 }
166
167 if ctx.ids.database_find_str != 0 && db_op == ctx.ids.database_find_str {
168 let col_call_no = p_i32(0);
169 let needle = p_str(1);
170 let out = db.find_str(col_call_no, needle).unwrap_or(-1);
171 ctx.push(Value::Int(out as i64));
172 return Ok(true);
173 }
174
175 if ctx.ids.database_find_str_real != 0 && db_op == ctx.ids.database_find_str_real {
176 let col_call_no = p_i32(0);
177 let needle = p_str(1);
178 let out = db.find_str_real(col_call_no, needle).unwrap_or(-1);
179 ctx.push(Value::Int(out as i64));
180 return Ok(true);
181 }
182
183 let prop_key = composite_db_op_key(db_no, db_op);
184 prop_access::store_or_push_prop(ctx, form_id, prop_key, chain_pos.unwrap(), args);
185 return Ok(true);
186 }
187
188 prop_access::store_or_push_prop(ctx, form_id, op, chain_pos.unwrap(), args);
189 return Ok(true);
190 }
191
192 Ok(false)
193}