Skip to main content

siglus_scene_vm/runtime/forms/
bgm_table.rs

1use 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(&regist_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}