pub mod analysis;
pub mod assembly;
pub mod instruction;
mod name_validation;
pub(crate) mod opcodes;
use std::ops::Range;
pub use crate::gml::instruction::Instruction;
use crate::prelude::*;
#[derive(Debug, Clone, PartialEq)]
pub struct GMCode {
pub name: String,
pub instructions: Vec<Instruction>,
pub modern_data: Option<ModernData>,
}
impl GMCode {
pub fn find_children_by_name(&self, data: &GMData) -> Result<Vec<GMRef<Self>>> {
if data.general_info.wad_version < 15 {
return Ok(Vec::new());
}
let mut children: Vec<GMRef<Self>> = Vec::new();
for (idx, code_entry) in data.codes.iter().enumerate() {
let Some(parent) = code_entry.parent() else {
continue;
};
let parent = data.codes.by_ref(parent)?;
if self.name == parent.name {
children.push(GMRef::from(idx));
}
}
Ok(children)
}
#[must_use]
pub fn find_children(code_ref: GMRef<Self>, data: &GMData) -> Vec<GMRef<Self>> {
if data.general_info.wad_version < 15 {
return Vec::new();
}
let mut children: Vec<GMRef<Self>> = Vec::new();
for (idx, code_entry) in data.codes.iter().enumerate() {
if code_entry.parent() == Some(code_ref) {
children.push(GMRef::from(idx));
}
}
children
}
#[must_use]
pub fn length(&self) -> u32 {
instructions_size(&self.instructions)
}
#[must_use]
pub const fn parent(&self) -> Option<GMRef<Self>> {
match &self.modern_data {
Some(data) => data.parent,
None => None,
}
}
#[must_use]
pub const fn is_root(&self) -> bool {
self.parent().is_none()
}
#[must_use]
pub const fn execution_offset(&self) -> u32 {
match &self.modern_data {
Some(data) => data.execution_offset,
None => 0,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ModernData {
pub local_count: u16,
pub argument_count: u16,
pub weird_local_flag: bool,
pub execution_offset: u32,
pub parent: Option<GMRef<GMCode>>,
}
#[must_use]
pub fn instructions_size(instructions: &[Instruction]) -> u32 {
let mut size: u32 = 0;
for instruction in instructions {
size += instruction.size();
}
size
}
fn splice_instructions(
haystack: &mut Vec<Instruction>,
range: Range<u32>,
replace_with: &[Instruction],
needs_result: bool,
) -> Result<Option<Vec<Instruction>>> {
let start = range.start as usize;
let end = range.end as usize;
let len = haystack.len();
if start >= len {
bail!("Start index {start} out of bounds for vector with instruction count {len}");
}
if start > end {
bail!("Start index {start} is greater than the end index {end}");
}
let insertion_size = instructions_size(replace_with);
let removal_size = instructions_size(&haystack[start..end]);
let fixed_offset = (insertion_size - removal_size) as i32 / 4;
let first_half_size = instructions_size(&haystack[..start]) as i32 / 4;
let mut cur_pos: u32 = 0;
for (i, instr) in haystack.iter_mut().enumerate() {
cur_pos += instr.size4();
let Some(offset) = instr.jump_offset_mut() else {
continue;
};
let branch_target_pos = cur_pos as i32 + *offset;
let origin_is_first_half = i < start;
let target_is_first_half = branch_target_pos < first_half_size;
if origin_is_first_half == target_is_first_half {
continue;
}
if origin_is_first_half {
*offset += fixed_offset;
} else {
*offset -= fixed_offset;
}
}
let iter = haystack.splice(start..end, replace_with.iter().cloned());
if needs_result {
Ok(Some(iter.collect()))
} else {
Ok(None)
}
}
pub fn insert_instructions(
haystack: &mut Vec<Instruction>,
index: u32,
insertion: &[Instruction],
) -> Result<()> {
splice_instructions(haystack, index..index, insertion, false).with_context(|| {
format!(
"inserting {} instructions at index {} into vector with {} instructions",
insertion.len(),
index,
haystack.len(),
)
})?;
Ok(())
}
pub fn insert_instruction(
haystack: &mut Vec<Instruction>,
index: u32,
insertion: &Instruction,
) -> Result<()> {
insert_instructions(haystack, index, std::slice::from_ref(insertion))
.with_context(|| format!("inserting single instruction {insertion:?}"))
}
#[allow(clippy::missing_panics_doc)]
pub fn remove_instructions(
haystack: &mut Vec<Instruction>,
range: Range<u32>,
) -> Result<Vec<Instruction>> {
let removal_len = range.len();
let index = range.start;
let old_instrs = splice_instructions(haystack, range, &[], true).with_context(|| {
format!(
"removing {} instructions at index {} of vector with {} instructions",
removal_len,
index,
haystack.len(),
)
})?;
Ok(old_instrs.unwrap())
}
pub fn remove_instruction(haystack: &mut Vec<Instruction>, index: u32) -> Result<Vec<Instruction>> {
remove_instructions(haystack, index..index + 1)
}