#[derive(Debug, PartialEq, Eq)]
pub struct LiteralScriptStep {
pub next: usize,
pub operation: LiteralScriptOperation,
}
#[derive(Debug, PartialEq, Eq)]
pub enum LiteralScriptOperation {
Task {
function_id: u16,
arguments: Vec<i32>,
},
End { target: u32 },
}
pub fn decode_literal_script_step(code: &[u8], offset: usize) -> Result<LiteralScriptStep, String> {
let mut cursor = offset;
let mut read = |count: usize| {
let end = cursor
.checked_add(count)
.ok_or_else(|| format!("script instruction {offset}: operand offset overflow"))?;
let bytes = code
.get(cursor..end)
.ok_or_else(|| format!("script instruction {offset}: truncated operand at {cursor}"))?;
cursor = end;
Ok::<_, String>(bytes)
};
let opcode = read(1)?[0];
let operation = match opcode {
0 => {
let bytes = read(2)?;
let function_id = u16::from_le_bytes([bytes[0], bytes[1]]);
let count = if function_id == 0 { 0 } else { read(1)?[0] };
let mut arguments = Vec::with_capacity(usize::from(count));
for _ in 0..count {
let operand = read(1)?[0];
if !matches!(operand, 7 | 22) {
return Err(format!(
"script instruction {offset}: nonliteral task operand {operand} unsupported"
));
}
let bytes = read(4)?;
arguments.push(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]));
}
LiteralScriptOperation::Task {
function_id,
arguments,
}
}
5 => {
let bytes = read(4)?;
LiteralScriptOperation::End {
target: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
}
}
_ => {
return Err(format!(
"script instruction {offset}: opcode {opcode} unsupported by literal task decoder"
));
}
};
Ok(LiteralScriptStep {
next: cursor,
operation,
})
}