mod chapter1_old;
mod chapter3;
mod deltarune;
mod demo_lts_ch1;
mod demo_lts_ch2;
mod demo_prelts;
mod undertale;
use crate::gml::GMCode;
use crate::gml::instruction::InstanceType;
use crate::gml::instruction::Instruction;
use crate::prelude::*;
use crate::wad::elements::variable::GMVariable;
impl GMData {
pub fn toggle_debug(&mut self, enable: bool) -> Result<()> {
toggle_debug(self, enable).with_context(|| format!("toggling debug mode to {enable}"))
}
pub fn enable_debug(&mut self) -> Result<()> {
toggle_debug(self, true).context("enabling debug mode")
}
pub fn disable_debug(&mut self) -> Result<()> {
toggle_debug(self, false).context("disabling debug mode")
}
}
fn toggle_debug(data: &mut GMData, enable: bool) -> Result<()> {
let gen8 = &data.general_info;
let display_name: &str = &gen8.display_name;
let internal_name: &str = &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").is_ok() {
deltarune::toggle(data, enable)
} else if gen8.version.is_version_at_least((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").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<GMCode>,
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: &GMVariable = data.variables.by_ref(variable.variable)?;
if gm_variable.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<GMCode>,
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 GMCode = data.codes.by_ref_mut(code_ref)?;
let integer = i16::from(enable);
code.instructions[instruction_index] = Instruction::PushImmediate { integer };
Ok(())
}