siglus_scene_vm/runtime/forms/
bgm_table.rs1use anyhow::Result;
2
3use crate::runtime::{CommandContext, Value};
4
5use super::codes::bgm_table_op;
6
7fn store_or_push_bgm_table_prop(ctx: &mut CommandContext, op: i32, args: &[Value]) {
8 let form_key = if ctx.ids.form_global_bgm_table != 0 {
9 ctx.ids.form_global_bgm_table
10 } else {
11 super::codes::FORM_GLOBAL_BGM_TABLE
12 };
13 let prop = op;
14 if let Some(v) = args.get(1).cloned() {
15 match v {
16 Value::Str(s) => {
17 ctx.globals
18 .str_props
19 .entry(form_key)
20 .or_default()
21 .insert(prop, s);
22 }
23 Value::Int(n) => {
24 ctx.globals
25 .int_props
26 .entry(form_key)
27 .or_default()
28 .insert(prop, n);
29 }
30 _ => {}
31 }
32 ctx.push(Value::Int(0));
33 return;
34 }
35 if let Some(s) = ctx
36 .globals
37 .str_props
38 .get(&form_key)
39 .and_then(|m| m.get(&prop))
40 .cloned()
41 {
42 ctx.push(Value::Str(s));
43 return;
44 }
45 let v = ctx
46 .globals
47 .int_props
48 .get(&form_key)
49 .and_then(|m| m.get(&prop).copied())
50 .unwrap_or(0);
51 ctx.push(Value::Int(v));
52}
53
54fn trim_args(args: &[Value]) -> &[Value] {
55 if args.len() >= 3
56 && matches!(args[args.len() - 3], Value::Element(_))
57 && matches!(args[args.len() - 2], Value::Int(_))
58 && matches!(args[args.len() - 1], Value::Int(_))
59 {
60 &args[..args.len() - 3]
61 } else {
62 args
63 }
64}
65
66fn arg_str<'a>(args: &'a [Value], idx: usize) -> Option<&'a str> {
67 match args.get(idx) {
68 Some(Value::Str(s)) => Some(s.as_str()),
69 Some(Value::NamedArg { value, .. }) => value.as_str(),
70 _ => None,
71 }
72}
73
74fn arg_int(args: &[Value], idx: usize) -> Option<i64> {
75 args.get(idx).and_then(|v| v.as_i64())
76}
77
78fn normalize_name(name: &str) -> String {
79 name.trim().to_ascii_lowercase()
80}
81
82fn bgm_declared_count(ctx: &CommandContext) -> usize {
83 ctx.tables
84 .gameexe
85 .as_ref()
86 .map(|cfg| {
87 cfg.get_usize("BGM.CNT")
88 .unwrap_or_else(|| cfg.indexed_count("BGM"))
89 })
90 .unwrap_or(0)
91}
92
93fn bgm_count(ctx: &CommandContext) -> usize {
94 bgm_declared_count(ctx)
95 .max(ctx.globals.bgm_table_flags.len())
96 .max(ctx.globals.bgm_table_listened.len())
97}
98
99fn ensure_bgm_flags_size(ctx: &mut CommandContext) {
100 let count = bgm_declared_count(ctx);
101 if ctx.globals.bgm_table_flags.len() < count {
102 ctx.globals
103 .bgm_table_flags
104 .resize(count, ctx.globals.bgm_table_all_flag);
105 }
106}
107
108fn bgm_regist_name(ctx: &CommandContext, index: usize) -> Option<String> {
109 let cfg = ctx.tables.gameexe.as_ref()?;
110 cfg.get_indexed_item_unquoted("BGM", index, 0)
111 .or_else(|| cfg.get_indexed_field_unquoted("BGM", index, "REGIST_NAME"))
112 .or_else(|| cfg.get_indexed_unquoted("BGM", index))
113 .map(|s| s.to_string())
114}
115
116fn bgm_no_by_regist_name(ctx: &CommandContext, name: &str) -> Option<usize> {
117 let needle = normalize_name(name);
118 let count = bgm_declared_count(ctx);
119 for i in 0..count {
120 let Some(regist_name) = bgm_regist_name(ctx, i) else {
121 continue;
122 };
123 if normalize_name(®ist_name) == needle {
124 return Some(i);
125 }
126 }
127 None
128}
129
130pub(crate) fn mark_listened_by_name(ctx: &mut CommandContext, name: &str, listened: bool) -> bool {
131 ensure_bgm_flags_size(ctx);
132 let key = normalize_name(name);
133 let index = bgm_no_by_regist_name(ctx, name);
134 if let Some(index) = index {
135 if ctx.globals.bgm_table_flags.len() <= index {
136 ctx.globals
137 .bgm_table_flags
138 .resize(index + 1, ctx.globals.bgm_table_all_flag);
139 }
140 ctx.globals.bgm_table_flags[index] = listened;
141 ctx.globals.bgm_table_listened.insert(key, listened);
142 true
143 } else {
144 false
145 }
146}
147
148pub fn dispatch(ctx: &mut CommandContext, args: &[Value]) -> Result<bool> {
149 let args = trim_args(args);
150 let Some(op) = args.get(0).and_then(|v| v.as_i64()).map(|v| v as i32) else {
151 ctx.push(Value::Int(0));
152 return Ok(true);
153 };
154
155 match op {
156 bgm_table_op::GET_COUNT => {
157 ctx.push(Value::Int(bgm_count(ctx) as i64));
158 Ok(true)
159 }
160 bgm_table_op::GET_LISTEN_BY_NAME => {
161 let Some(name) = arg_str(args, 1) else {
162 ctx.push(Value::Int(-1));
163 return Ok(true);
164 };
165 ensure_bgm_flags_size(ctx);
166 let res = if let Some(index) = bgm_no_by_regist_name(ctx, name) {
167 ctx.globals
168 .bgm_table_flags
169 .get(index)
170 .copied()
171 .unwrap_or(ctx.globals.bgm_table_all_flag) as i64
172 } else {
173 -1
174 };
175 ctx.push(Value::Int(res));
176 Ok(true)
177 }
178 bgm_table_op::SET_LISTEN_CURRENT => {
179 let Some(name) = arg_str(args, 1) else {
180 ctx.push(Value::Int(0));
181 return Ok(true);
182 };
183 let listened = arg_int(args, 2).unwrap_or(0) != 0;
184 let _ = mark_listened_by_name(ctx, name, listened);
185 ctx.push(Value::Int(0));
186 Ok(true)
187 }
188 bgm_table_op::SET_ALL_FLAG => {
189 let listened = arg_int(args, 1).unwrap_or(0) != 0;
190 ctx.globals.bgm_table_all_flag = listened;
191 ensure_bgm_flags_size(ctx);
192 for v in &mut ctx.globals.bgm_table_flags {
193 *v = listened;
194 }
195 for v in ctx.globals.bgm_table_listened.values_mut() {
196 *v = listened;
197 }
198 ctx.push(Value::Int(0));
199 Ok(true)
200 }
201 _ => {
202 store_or_push_bgm_table_prop(ctx, op, args);
203 Ok(true)
204 }
205 }
206}