mod chapter1_old;
mod chapter3;
mod deltarune;
mod demo_lts_ch1;
mod demo_lts_ch2;
mod demo_prelts;
mod undertale;
use crate::gml::Code;
use crate::gml::instruction::InstanceType;
use crate::gml::instruction::Instruction;
use crate::prelude::*;
use crate::wad::elem::variable::Variable;
impl GMData {
pub fn toggle_debug(&mut self, enable: bool) -> Result<()> {
toggle_debug(self, enable).ctx(|| format!("toggling debug mode to {enable}"))
}
pub fn enable_debug(&mut self) -> Result<()> {
toggle_debug(self, true).ctx("enabling debug mode")
}
pub fn disable_debug(&mut self) -> Result<()> {
toggle_debug(self, false).ctx("disabling debug mode")
}
}
fn toggle_debug(data: &mut GMData, enable: bool) -> Result<()> {
let gen8 = &data.general_info;
let display_name: &str = data.strings.by_ref(gen8.display_name)?;
let internal_name: &str = data.strings.by_ref(gen8.game_name)?;
if internal_name.contains("UNDERTALE") || internal_name == "NXTALE" {
return undertale::toggle(data, enable);
}
match display_name {
"SURVEY_PROGRAM" => return chapter1_old::toggle(data, enable),
"DELTARUNE Chapter 1&2" => return demo_prelts::toggle(data, enable),
"DELTARUNE Chapter 3" => return chapter3::toggle(data, enable),
"DELTARUNE Chapter 4" => return deltarune::toggle(data, enable),
_ => {}
}
if display_name == "DELTARUNE Chapter 1" {
return if data
.game_objects
.by_name("obj_event_manager", &data.strings)
.is_ok()
{
deltarune::toggle(data, enable)
} else if gen8.version >= (2, 3) {
demo_lts_ch1::toggle(data, enable)
} else {
chapter1_old::toggle(data, enable)
};
}
if display_name == "DELTARUNE Chapter 2" {
return if data
.game_objects
.by_name("obj_event_manager", &data.strings)
.is_ok()
{
deltarune::toggle(data, enable)
} else {
demo_lts_ch2::toggle(data, enable)
};
}
bail!("Could not detect Undertale or Deltarune from game {display_name:?}");
}
fn find_debug(
data: &GMData,
code_ref: GMRef<Code>,
instance_type: InstanceType,
) -> Result<(usize, bool)> {
let code = data.codes.by_ref(code_ref)?;
for i in 0..code.instructions.len() - 1 {
let potential_push: &Instruction = &code.instructions[i];
let potential_pop: &Instruction = &code.instructions[i + 1];
let Instruction::Pop { variable, .. } = &potential_pop else {
continue;
};
if variable.instance_type != instance_type {
continue;
}
let gm_variable: &Variable = data.variables.by_ref(variable.variable)?;
let var_name = data.strings.by_ref(gm_variable.name)?;
if var_name != "debug" {
continue;
}
let Some(push_value) = potential_push.push_value() else {
bail!("Expected Instruction before Pop to be a Push, found {potential_push:?}")
};
let Some(is_enabled) = push_value.as_bool() else {
bail!(
"Push Instruction value does not have a proper boolean representation: \
{push_value:?}"
);
};
return Ok((i, is_enabled));
}
Err("Could not find any pop instruction for debug variable".into())
}
fn replace_debug(
data: &mut GMData,
code_ref: GMRef<Code>,
enable: bool,
instance_type: InstanceType,
) -> Result<()> {
let (instruction_index, is_enabled) = find_debug(data, code_ref, instance_type)?;
if enable == is_enabled {
return Ok(());
}
let code: &mut Code = data.codes.by_ref_mut(code_ref)?;
let integer = i16::from(enable);
code.instructions[instruction_index] = Instruction::PushImmediate { integer };
Ok(())
}